public void Remove(GenericInstanceMethod genericInstanceMethod) { this.Remove(genericInstanceMethod.ElementMethod.FullName, genericInstanceMethod); }
public static List <IType> GetParamList(this MethodReference def, ILRuntime.Runtime.Enviorment.AppDomain appdomain, IType contextType, IMethod contextMethod, IType[] genericArguments) { if (def.HasParameters) { List <IType> param = new List <IType>(); var dt = appdomain.GetType(def.DeclaringType, contextType, contextMethod); foreach (var i in def.Parameters) { IType t = null; t = appdomain.GetType(i.ParameterType, dt, null); if (t == null && def.IsGenericInstance) { GenericInstanceMethod gim = (GenericInstanceMethod)def; string name = i.ParameterType.IsByReference ? i.ParameterType.GetElementType().FullName : i.ParameterType.FullName; for (int j = 0; j < gim.GenericArguments.Count; j++) { var gp = gim.ElementMethod.GenericParameters[j]; var ga = gim.GenericArguments[j]; if (name == gp.Name) { t = appdomain.GetType(ga, contextType, contextMethod); if (t == null && genericArguments != null) { t = genericArguments[j]; } break; } else if (name.Contains(gp.Name)) { t = appdomain.GetType(ga, contextType, contextMethod); if (t == null && genericArguments != null) { t = genericArguments[j]; } if (name == gp.Name) { name = t.FullName; } else if (name == gp.Name + "[]") { name = t.FullName + "[]"; } else { /*name = name.Replace("<" + gp.Name + ">", "<" + ga.FullName + ">"); * name = name.Replace("<" + gp.Name + "[", "<" + ga.FullName + "["); * name = name.Replace("<" + gp.Name + ",", "<" + ga.FullName + ","); * name = name.Replace("," + gp.Name + ">", "," + ga.FullName + ">"); * name = name.Replace("," + gp.Name + "[", "," + ga.FullName + "["); * name = name.Replace("," + gp.Name + ",", "," + ga.FullName + ","); * name = name.Replace("," + gp.Name + "[", "," + ga.FullName + "[");*/ name = ReplaceGenericArgument(name, gp.Name, ga.FullName); } t = null; } } if (t == null) { t = appdomain.GetType(name); } } param.Add(t); } return(param); } else { return(EmptyParamList); } }
internal static TypeReference GetTypeWithGenericResolved( this GenericInstanceType genericInstanceParameter, GenericInstanceType genericInstanceType, GenericInstanceMethod genericInstanceMethod) { GenericInstanceType newGenericInstanceType = new GenericInstanceType(genericInstanceParameter.ElementType); foreach (var genericArg in genericInstanceParameter.GenericArguments) { TypeReference newGenericArg = genericArg; bool isByReference = false; bool isArray = false; int arrayRank = 0; if (newGenericArg.IsByReference) { isByReference = true; ByReferenceType byReferenceType = (ByReferenceType)newGenericArg; newGenericArg = byReferenceType.ElementType; } if (newGenericArg.IsArray) { isArray = true; ArrayType arrayType = (ArrayType)newGenericArg; arrayRank = arrayType.Rank; newGenericArg = arrayType.ElementType; } if (newGenericArg.IsGenericInstance) { newGenericArg = ((GenericInstanceType)newGenericArg).GetTypeWithGenericResolved(genericInstanceType, genericInstanceMethod); } if (newGenericArg.IsGenericParameter) { GenericParameter tmp = newGenericArg as GenericParameter; if (tmp.DeclaringType != null && genericInstanceType != null) { int position = genericInstanceType.ElementType.GenericParameters.GetIndexByName(newGenericArg.Name); if (position != -1) { newGenericArg = genericInstanceType.GenericArguments[position]; } } else if (tmp.DeclaringMethod != null && genericInstanceMethod != null) { int position = genericInstanceMethod.GetElementMethod().GenericParameters.GetIndexByName(newGenericArg.Name); if (position != -1) { newGenericArg = genericInstanceMethod.GenericArguments[position]; } } } if (isArray) { newGenericArg = new ArrayType(newGenericArg, arrayRank); } if (isByReference) { newGenericArg = new ByReferenceType(newGenericArg); } newGenericInstanceType.GenericArguments.Add(newGenericArg); } return(newGenericInstanceType); }
public TypeResolver(GenericInstanceMethod methodDefinitionContext) { this._methodDefinitionContext = methodDefinitionContext; }
public static PropertyDefinition BuildPropertyCore(Property property, TypeDefinition type) { var mod = type.Module; //从缓存中查找clr type var ptype = property.PropertyType.GetTypeReference(); var fieldInfo = new FieldDefinition("_" + property.称, FieldAttributes.Private, ptype); type.Fields.Add(fieldInfo); // type.DefineField("_" + property.名称, ptype, FieldAttributes.Private); var propertyInfo = new PropertyDefinition(property.称, PropertyAttributes.None, ptype) { HasThis = true }; type.Properties.Add(propertyInfo); #region 基本属性设置 if (property.Size != 100 && property.Size != 0) { propertyInfo.Size(property.Size); } //if (!string.IsNullOrEmpty(property.Expression)) //{ // propertyInfo.PersistentAlias(property.Expression); //} #endregion ; #region getter //.method public hidebysig specialname instance string get_创建者() cil managedMono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.SpecialName var attr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.FamANDAssem | MethodAttributes.Family; var setattr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.FamANDAssem | MethodAttributes.Family; var getMethod = new MethodDefinition("get_" + property.称, attr, ptype); var getMethodIl = getMethod.Body.GetILProcessor(); getMethod.Body.MaxStackSize = 8; getMethod.Body.InitLocals = true; getMethodIl.Emit(OpCodes.Ldarg_0); getMethodIl.Emit(OpCodes.Ldfld, fieldInfo); getMethodIl.Emit(OpCodes.Ret); type.Methods.Add(getMethod); propertyInfo.GetMethod = getMethod; #endregion #region 非计算类型 if (string.IsNullOrEmpty(property.Expression)) { var set = new MethodDefinition("set_" + property.称, setattr, mod.ImportReference(typeof(void))); var para = new ParameterDefinition("value", ParameterAttributes.None, ptype); set.Parameters.Add(para); var setPropertyValue = mod.ImportReference( typeof(PersistentBase) .GetMethods( SR.BindingFlags.InvokeMethod | SR.BindingFlags.NonPublic | SR.BindingFlags.Instance) .Single(x => x.Name.StartsWith("SetPropertyValue") && x.IsGenericMethod && x.GetParameters().Length == 3) ); var setPropertyValueMethod = new GenericInstanceMethod(setPropertyValue); setPropertyValueMethod.GenericArguments.Add(ptype); //setPropertyValue = setPropertyValue.MakeGenericMethod(ptype); var setIl = set.Body.Instructions; setIl.Add(Instruction.Create(OpCodes.Ldarg_0)); setIl.Add(Instruction.Create(OpCodes.Ldstr, property.称)); setIl.Add(Instruction.Create(OpCodes.Ldarg_0)); setIl.Add(Instruction.Create(OpCodes.Ldflda, fieldInfo)); setIl.Add(Instruction.Create(OpCodes.Ldarg_1)); setIl.Add(Instruction.Create(OpCodes.Call, setPropertyValueMethod)); setIl.Add(Instruction.Create(OpCodes.Pop)); setIl.Add(Instruction.Create(OpCodes.Ret)); propertyInfo.SetMethod = set; type.Methods.Add(set); } #endregion return(propertyInfo); }
/// <summary> /// Get method signature of invoked method. It will be resolved with /// generic arguments while the method is invoked. /// </summary> /// <param name="method">Method to get resolved signature.</param> /// <param name="methodDef">Method definition of method parameter.</param> /// <returns> /// Method signature with generic parameters resolved, e.g., List<string> /// Generic parameters are resolved to argument values. /// </returns> public static string GetResolvedMethodSignature(this MethodReference method, MethodDefinition methodDef) { string typeSignature = GetResolvedTypeSignature(method.DeclaringType); string methodSignature; if (method.Name.StartsWith(GetPrefix)) { methodSignature = $"{method.Name.Substring(GetPrefix.Length)}.get"; } else if (method.Name.StartsWith(SetPrefix)) { methodSignature = $"{method.Name.Substring(SetPrefix.Length)}.set"; } else { methodSignature = method.Name; } if (method.IsGenericInstance) { GenericInstanceMethod genericInstance = (GenericInstanceMethod)method; var genericParameters = genericInstance.GenericArguments.Select(genericArgument => genericArgument.GetResolvedTypeSignature()); methodSignature = genericParameters.Count() != 0 ? $"{methodSignature}<{string.Join(",", genericParameters)}>" : methodSignature; } IList <string> parameters = new List <string>(); if (method.IsGenericInstance) { GenericInstanceMethod genericInstanceMethod = (GenericInstanceMethod)method; for (int i = 0; i < method.Parameters.Count; i++) { ParameterDefinition parameter = method.Parameters[i]; ParameterDefinition parameterOfMethodDef = methodDef.Parameters[i]; TypeReference resolvedParameterType = parameter.GetTypeWithGenericResolved(genericInstanceMethod); if (resolvedParameterType.IsByReference) { ByReferenceType byReference = resolvedParameterType as ByReferenceType; string prefix; if (parameterOfMethodDef.IsOut) { prefix = "out"; } else if (parameterOfMethodDef.IsIn) { prefix = "in"; } else { prefix = "ref"; } parameters.Add($"{prefix}{byReference.ElementType.GetResolvedTypeSignature()}"); } else if (parameterOfMethodDef.IsParams()) { parameters.Add($"params{resolvedParameterType.GetResolvedTypeSignature()}"); } else { parameters.Add(resolvedParameterType.GetResolvedTypeSignature()); } } } else { for (int i = 0; i < method.Parameters.Count; i++) { ParameterDefinition parameter = method.Parameters[i]; ParameterDefinition parameterOfMethodDef = methodDef.Parameters[i]; TypeReference resolvedParameterType = parameter.GetTypeWithGenericResolved(); if (resolvedParameterType.IsByReference) { ByReferenceType byReference = resolvedParameterType as ByReferenceType; string prefix; if (parameterOfMethodDef.IsOut) { prefix = "out"; } else if (parameterOfMethodDef.IsIn) { prefix = "in"; } else { prefix = "ref"; } parameters.Add($"{prefix}{byReference.ElementType.GetResolvedTypeSignature()}"); } else if (parameterOfMethodDef.IsParams()) { parameters.Add($"params{resolvedParameterType.GetResolvedTypeSignature()}"); } else { parameters.Add(resolvedParameterType.GetResolvedTypeSignature()); } } } return($"{typeSignature}.{methodSignature}({string.Join(",", parameters)})"); }
void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize) { // check for Hook function if (!SyncVarProcessor.CheckForHookFunction(netBehaviourSubclass, syncVar, out MethodDefinition foundMethod)) { return; } // [SyncVar] GameObject/NetworkIdentity? /* * Generates code like: * uint oldNetId = ___qNetId; * GameObject oldSyncVar = syncvar.getter; // returns GetSyncVarGameObject(___qNetId) * ___qNetId = reader.ReadPackedUInt32(); * if (!SyncVarEqual(oldNetId, ref ___goNetId)) * { * OnSetQ(oldSyncVar, syncvar.getter); // getter returns GetSyncVarGameObject(___qNetId) * } */ 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; VariableDefinition 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; VariableDefinition 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. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // put 'this.' onto stack for 'this.netId' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // reader. for 'reader.Read()' below serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint32Type))); // Read() serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField)); // netId if (foundMethod != 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 GenericInstanceMethod 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 serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldSyncVar)); // oldSyncVar GO/NI serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); // syncvar.get (finds current GO/NI from netId) serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod)); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } } // [SyncVar] int/float/struct/etc.? /* * Generates code like: * int oldValue = a; // for hook * 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; VariableDefinition 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. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // put 'this.' onto stack for 'this.syncvar' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // reader. for 'reader.Read()' below serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); // reader.Read() serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); // syncvar if (foundMethod != 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 GenericInstanceMethod 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 serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); // oldvalue serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); // syncvar.get serWorker.Append(serWorker.Create(OpCodes.Call, foundMethod)); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } } }
public void FindCoreReferences() { var types = new List <TypeDefinition>(); AddAssemblyIfExists("mscorlib", types); AddAssemblyIfExists("System", types); AddAssemblyIfExists("System.Runtime", types); AddAssemblyIfExists("System.Core", types); AddAssemblyIfExists("netstandard", types); AddAssemblyIfExists("System.ObjectModel", types); AddAssemblyIfExists("System.Threading", types); var objectDefinition = types.First(x => x.Name == "Object"); var constructorDefinition = objectDefinition.Methods.First(x => x.IsConstructor); ObjectConstructor = ModuleDefinition.ImportReference(constructorDefinition); var objectEqualsMethodDefinition = objectDefinition.Methods.First(x => x.Name == "Equals" && x.Parameters.Count == 2); ObjectEqualsMethod = ModuleDefinition.ImportReference(objectEqualsMethodDefinition); var nullableDefinition = types.FirstOrDefault(x => x.Name == "Nullable"); NullableEqualsMethod = ModuleDefinition.ImportReference(nullableDefinition).Resolve().Methods.First(x => x.Name == "Equals"); EqualityComparerTypeReference = types.FirstOrDefault(x => x.Name == "EqualityComparer`1"); var actionDefinition = types.First(x => x.Name == "Action"); ActionTypeReference = ModuleDefinition.ImportReference(actionDefinition); var actionConstructor = actionDefinition.Methods.First(x => x.IsConstructor); ActionConstructorReference = ModuleDefinition.ImportReference(actionConstructor); var propChangedInterfaceDefinition = types.First(x => x.Name == "INotifyPropertyChanged"); PropChangedInterfaceReference = ModuleDefinition.ImportReference(propChangedInterfaceDefinition); var propChangedHandlerDefinition = types.First(x => x.Name == "PropertyChangedEventHandler"); PropChangedHandlerReference = ModuleDefinition.ImportReference(propChangedHandlerDefinition); ComponentModelPropertyChangedEventHandlerInvokeReference = ModuleDefinition.ImportReference(propChangedHandlerDefinition.Methods.First(x => x.Name == "Invoke")); var propChangedArgsDefinition = types.First(x => x.Name == "PropertyChangedEventArgs"); ComponentModelPropertyChangedEventConstructorReference = ModuleDefinition.ImportReference(propChangedArgsDefinition.Methods.First(x => x.IsConstructor)); var delegateDefinition = types.First(x => x.Name == "Delegate"); var combineMethodDefinition = delegateDefinition.Methods .Single(x => x.Name == "Combine" && x.Parameters.Count == 2 && x.Parameters.All(p => p.ParameterType == delegateDefinition)); DelegateCombineMethodRef = ModuleDefinition.ImportReference(combineMethodDefinition); var removeMethodDefinition = delegateDefinition.Methods.First(x => x.Name == "Remove"); DelegateRemoveMethodRef = ModuleDefinition.ImportReference(removeMethodDefinition); var interlockedDefinition = types.First(x => x.FullName == "System.Threading.Interlocked"); var genericCompareExchangeMethodDefinition = interlockedDefinition .Methods.First(x => x.IsStatic && x.Name == "CompareExchange" && x.GenericParameters.Count == 1 && x.Parameters.Count == 3); var genericCompareExchangeMethod = ModuleDefinition.ImportReference(genericCompareExchangeMethodDefinition); InterlockedCompareExchangeForPropChangedHandler = new GenericInstanceMethod(genericCompareExchangeMethod); InterlockedCompareExchangeForPropChangedHandler.GenericArguments.Add(PropChangedHandlerReference); }
private void ProcessMethod(TypeDefinition type, MethodDefinition method, ICodeGenerator generator) { List <ParameterDefinition> dependencyParameters = method.Parameters.Where( p => p.CustomAttributes.Any(a => a.AttributeType.IsType(Import.AutoDI.DependencyAttributeType))).ToList(); List <PropertyDefinition> dependencyProperties = method.IsConstructor ? type.Properties.Where(p => p.CustomAttributes.Any(a => a.AttributeType.IsType(Import.AutoDI.DependencyAttributeType))).ToList() : new List <PropertyDefinition>(); if (dependencyParameters.Any() || dependencyProperties.Any()) { Logger.Debug($"Processing method '{method.Name}' for '{method.DeclaringType.FullName}'", DebugLogLevel.Verbose); var injector = new Injector(method); IMethodGenerator methodGenerator = generator?.Method(method); foreach (ParameterDefinition parameter in dependencyParameters) { if (!parameter.IsOptional) { Logger.Info( $"Constructor parameter {parameter.ParameterType.Name} {parameter.Name} is marked with {Import.AutoDI.DependencyAttributeType.FullName} but is not an optional parameter. In {type.FullName}."); } if (parameter.Constant != null) { Logger.Warning( $"Constructor parameter {parameter.ParameterType.Name} {parameter.Name} in {type.FullName} does not have a null default value. AutoDI will only resolve dependencies that are null"); } var initInstruction = Instruction.Create(OpCodes.Ldarg, parameter); var storeInstruction = Instruction.Create(OpCodes.Starg, parameter); ResolveDependency(parameter.ParameterType, parameter, new[] { initInstruction }, null, storeInstruction, parameter.Name); } foreach (PropertyDefinition property in dependencyProperties) { FieldDefinition backingField = null; //Store the return from the resolve method in the method parameter if (property.SetMethod == null) { //NB: Constant string, compiler detail... yuck yuck and double duck backingField = property.DeclaringType.Fields.FirstOrDefault(f => f.Name == $"<{property.Name}>k__BackingField"); if (backingField == null) { Logger.Warning( $"{property.FullName} is marked with {Import.AutoDI.DependencyAttributeType.FullName} but cannot be set. Dependency properties must either be auto properties or have a setter"); continue; } } //injector.Insert(OpCodes.Call, property.GetMethod); ResolveDependency(property.PropertyType, property, new[] { Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Call, property.GetMethod), }, Instruction.Create(OpCodes.Ldarg_0), property.SetMethod != null ? Instruction.Create(OpCodes.Call, property.SetMethod) : Instruction.Create(OpCodes.Stfld, backingField), property.Name); } methodGenerator?.Append($"//We now return you to your regularly scheduled method{Environment.NewLine}"); method.Body.OptimizeMacros(); void ResolveDependency(TypeReference dependencyType, ICustomAttributeProvider source, Instruction[] loadSource, Instruction resolveAssignmentTarget, Instruction setResult, string dependencyName) { //Push dependency parameter onto the stack if (methodGenerator != null) { methodGenerator.Append($"if ({dependencyName} == null)", loadSource.First()); methodGenerator.Append(Environment.NewLine + "{" + Environment.NewLine); } injector.Insert(loadSource); var afterParam = Instruction.Create(OpCodes.Nop); //Push null onto the stack injector.Insert(OpCodes.Ldnull); //Push 1 if the values are equal, 0 if they are not equal injector.Insert(OpCodes.Ceq); //Branch if the value is false (0), the dependency was set by the caller we wont replace it injector.Insert(OpCodes.Brfalse_S, afterParam); //Push the dependency resolver onto the stack if (resolveAssignmentTarget != null) { injector.Insert(resolveAssignmentTarget); } //Create parameters array var dependencyAttribute = source.CustomAttributes.First(x => x.AttributeType.IsType(Import.AutoDI.DependencyAttributeType)); var values = (dependencyAttribute.ConstructorArguments?.FirstOrDefault().Value as CustomAttributeArgument[]) ?.Select(x => x.Value) .OfType <CustomAttributeArgument>() .ToArray(); //Create array of appropriate length Instruction loadArraySize = injector.Insert(OpCodes.Ldc_I4, values?.Length ?? 0); if (methodGenerator != null) { methodGenerator.Append($" {dependencyName} = GlobalDI.GetService<{dependencyType.FullNameCSharp()}>();", resolveAssignmentTarget ?? loadArraySize); methodGenerator.Append(Environment.NewLine); } injector.Insert(OpCodes.Newarr, ModuleDefinition.ImportReference(Import.System.Object)); if (values?.Length > 0) { for (int i = 0; i < values.Length; ++i) { injector.Insert(OpCodes.Dup); //Push the array index to insert injector.Insert(OpCodes.Ldc_I4, i); //Insert constant value with any boxing/conversion needed InsertObjectConstant(injector, values[i].Value, values[i].Type.Resolve()); //Push the object into the array at index injector.Insert(OpCodes.Stelem_Ref); } } //Call the resolve method var getServiceMethod = new GenericInstanceMethod(Import.AutoDI.GlobalDI.GetService) { GenericArguments = { ModuleDefinition.ImportReference(dependencyType) } }; injector.Insert(OpCodes.Call, getServiceMethod); //Set the return from the resolve method into the parameter injector.Insert(setResult); injector.Insert(afterParam); if (methodGenerator != null) { methodGenerator.Append("}", afterParam); methodGenerator.Append(Environment.NewLine); } } } }
static void CreateConvertMethod(TypeDefinition authoringType, TypeDefinition componentDataType) { var moduleDefinition = componentDataType.Module; var entityTypeReference = moduleDefinition.ImportReference(typeof(Unity.Entities.Entity)); var entityManagerTypeReference = moduleDefinition.ImportReference(typeof(Unity.Entities.EntityManager)); var gameObjectTypeReference = moduleDefinition.ImportReference(typeof(UnityEngine.GameObject)); var convertMethod = new MethodDefinition("Convert", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual, moduleDefinition.TypeSystem.Void) { Parameters = { new ParameterDefinition("entity", ParameterAttributes.None, entityTypeReference), new ParameterDefinition("destinationManager", ParameterAttributes.None, entityManagerTypeReference), new ParameterDefinition("conversionSystem", ParameterAttributes.None, moduleDefinition.ImportReference(typeof(GameObjectConversionSystem))) } }; authoringType.Methods.Add(convertMethod); // Make a local variable which we'll populate with the values stored in the MonoBehaviour var variableDefinition = new VariableDefinition(componentDataType); convertMethod.Body.Variables.Add(variableDefinition); var ilProcessor = convertMethod.Body.GetILProcessor(); // Initialize the local variable. (we might not need this, but all c# compilers emit it, so let's play it safe for now ilProcessor.Emit(OpCodes.Ldloca_S, variableDefinition); ilProcessor.Emit(OpCodes.Initobj, componentDataType); var getPrimaryEntityGameObjectMethod = moduleDefinition.ImportReference( typeof(GameObjectConversionSystem).GetMethod("GetPrimaryEntity", new Type[] { typeof(UnityEngine.GameObject) })); var getPrimaryEntityComponentMethod = moduleDefinition.ImportReference( typeof(GameObjectConversionSystem).GetMethod("GetPrimaryEntity", new Type[] { typeof(UnityEngine.Component) })); // Let's transfer every field in the MonoBehaviour over to the corresponding field in the IComponentData foreach (var field in authoringType.Fields) { var destinationField = componentDataType.Fields.Single(f => f.Name == field.Name); // Load the local iComponentData we are populating, so we can later write to it ilProcessor.Emit(OpCodes.Ldloca_S, variableDefinition); if (destinationField.FieldType.TypeReferenceEquals(entityTypeReference)) { // conversionSystem.GetPrimaryEntity(myThing); ilProcessor.Emit(OpCodes.Ldarg_3); ilProcessor.Emit(OpCodes.Ldarg_0); ilProcessor.Emit(OpCodes.Ldfld, field); var methodToCall = field.FieldType.TypeReferenceEquals(gameObjectTypeReference) ? getPrimaryEntityGameObjectMethod : getPrimaryEntityComponentMethod; ilProcessor.Emit(OpCodes.Callvirt, methodToCall); } else { // Load this (our MonoBehaviour itself) ilProcessor.Emit(OpCodes.Ldarg_0); // Load the field in question from the MonoBehaviour ilProcessor.Emit(OpCodes.Ldfld, field); } // Store it to the IComponentData we already placed on the stack ilProcessor.Emit(OpCodes.Stfld, destinationField); } // Now that our local IComponentData is properly setup, the only thing left for us is to call: // entityManager.AddComponentData(entity, myPopulatedIComponentData). // IL method arguments go on the stack from first to last so: ilProcessor.Emit(OpCodes.Ldarg_2); //entityManager ilProcessor.Emit(OpCodes.Ldarg_1); //entity ilProcessor.Emit(OpCodes.Ldloc_0); //myPopulatedIComponentData // Build a MethodReference to EntityManager.AddComponentData<T>(Entity target, T payload); var addComponentDataMethodReference = new MethodReference("AddComponentData", moduleDefinition.TypeSystem.Void, entityManagerTypeReference) { HasThis = true, Parameters = { new ParameterDefinition("entity", ParameterAttributes.None, entityTypeReference), }, ReturnType = moduleDefinition.TypeSystem.Boolean }; var genericParameter = new GenericParameter("T", addComponentDataMethodReference); addComponentDataMethodReference.GenericParameters.Add(genericParameter); addComponentDataMethodReference.Parameters.Add(new ParameterDefinition("payload", ParameterAttributes.None, genericParameter)); // Since AddComponentData<T> is a generic method, we cannot call it super easily. // We have to wrap the generic method reference into a GenericInstanceMethod, // which let's us specify what we want to use for T for this specific invocation. // In our case T is the IComponentData we're operating on var genericInstanceMethod = new GenericInstanceMethod(addComponentDataMethodReference) { GenericArguments = { componentDataType }, }; ilProcessor.Emit(OpCodes.Callvirt, genericInstanceMethod); // Pop off return value since AddComponentData returns a bool ilProcessor.Emit(OpCodes.Pop); // We're done already! Easy peasy. ilProcessor.Emit(OpCodes.Ret); }
private Instruction CreateInstruction(MethodDefinition method, ILWeaver weaver, InstructionData instructionData, List <Instruction> insts, Patching.Patcher patcher) { OpCode opcode = opCodes[instructionData.OpCode]; OpType optype = instructionData.OpType; Instruction Instruction = null; int start; int end; switch (optype) { case OpType.None: Instruction = Instruction.Create(opcode); break; case OpType.Byte: Instruction = Instruction.Create(opcode, Convert.ToByte(instructionData.Operand)); break; case OpType.SByte: Instruction = Instruction.Create(opcode, Convert.ToSByte(instructionData.Operand)); break; case OpType.Int32: Instruction = Instruction.Create(opcode, Convert.ToInt32(instructionData.Operand)); break; case OpType.Int64: Instruction = Instruction.Create(opcode, Convert.ToInt64(instructionData.Operand)); break; case OpType.Single: Instruction = Instruction.Create(opcode, Convert.ToSingle(instructionData.Operand)); break; case OpType.Double: Instruction = Instruction.Create(opcode, Convert.ToDouble(instructionData.Operand)); break; case OpType.String: Instruction = Instruction.Create(opcode, Convert.ToString(instructionData.Operand)); break; case OpType.VerbatimString: Instruction = Instruction.Create(opcode, Regex.Unescape(Convert.ToString(instructionData.Operand))); break; case OpType.Instruction: int index = Convert.ToInt32(instructionData.Operand); Instruction = Instruction.Create(opcode, index < 1024 ? weaver.Instructions[index] : insts[index - 1024]); break; case OpType.Variable: Instruction = Instruction.Create(opcode, method.Body.Variables[Convert.ToInt32(instructionData.Operand)]); break; case OpType.Parameter: Instruction = Instruction.Create(opcode, method.Parameters[Convert.ToInt32(instructionData.Operand)]); break; case OpType.Field: string[] fieldData = Convert.ToString(instructionData.Operand).Split('|'); TypeDefinition fieldType = GetType(fieldData[0], fieldData[1], patcher); if (fieldType == null) { return(null); } FieldDefinition fieldField = fieldType.Fields.FirstOrDefault(f => f.Name.Equals(fieldData[2])); if (fieldField == null) { ShowMsg($"The Field '{fieldData[2]}' for '{Name}' could not be found!", "Missing Field", patcher); return(null); } Instruction = Instruction.Create(opcode, method.Module.Import(fieldField)); break; case OpType.Method: string[] methodData = Convert.ToString(instructionData.Operand).Split('|'); TypeDefinition methodType = GetType(methodData[0], methodData[1], patcher); if (methodType == null) { return(null); } if (methodData.Length > 3) { methodData[2] = string.Join("|", methodData.Skip(2).ToArray()); } MethodReference methodMethod; start = methodData[2].IndexOf('('); end = methodData[2].IndexOf(')'); if (start >= 0 && end >= 0 && start < end) { string name = TagsRegex.Replace(methodData[2], string.Empty).Trim(); string methodSig = methodData[2].Substring(start + 1, end - start - 1); string[] sigData = methodSig.Split(','); TypeDefinition[] sigTypes = new TypeDefinition[sigData.Length]; for (int i = 0; i < sigData.Length; i++) { string s = sigData[i]; string sigName = s.Trim(); string assem = "mscorlib"; if (sigName.Contains('|')) { string[] split = sigName.Split('|'); assem = split[0].Trim(); sigName = split[1].Trim(); } TypeDefinition sigType = GetType(assem, sigName, patcher); if (sigType == null) { ShowMsg($"SigType '{sigName}' not found", "Missing Method", patcher); return(null); } sigTypes[i] = sigType; } methodMethod = null; foreach (MethodDefinition methodDefinition in methodType.Methods) { if (!methodDefinition.Name.Equals(name) || methodDefinition.Parameters.Count != sigTypes.Length) { continue; } bool match = true; for (int i = 0; i < methodDefinition.Parameters.Count; i++) { ParameterDefinition parameter = methodDefinition.Parameters[i]; if (!parameter.ParameterType.FullName.Equals(sigTypes[i].FullName)) { match = false; break; } } if (!match) { continue; } methodMethod = methodDefinition; break; } } else { string methodName = methodData[2]; int position = methodName.IndexOf('['); if (position > 0) { methodName = methodName.Substring(0, position); } methodMethod = methodType.Methods.FirstOrDefault(f => { if (!f.Name.Equals(methodName)) { return(false); } if (position <= 0) { return(true); } return(f.HasGenericParameters); }); } if (methodMethod == null) { ShowMsg($"The Method '{methodData[2]}' for '{Name}' could not be found!", "Missing Method", patcher); return(null); } start = methodData[2].IndexOf('['); end = methodData[2].IndexOf(']'); if (start >= 0 && end >= 0 && start < end) { GenericInstanceMethod generic = new GenericInstanceMethod(methodMethod); string methodG = methodData[2].Substring(start + 1, end - start - 1); string[] genData = methodG.Split(','); TypeDefinition[] genTypes = new TypeDefinition[genData.Length]; for (int i = 0; i < genData.Length; i++) { string s = genData[i]; string genName = s.Trim(); string assem = "mscorlib"; if (genName.Contains('|')) { string[] split = genName.Split('|'); assem = split[0].Trim(); genName = split[1].Trim(); } TypeDefinition genType = GetType(assem, genName, patcher); if (genType == null) { ShowMsg($"GenericType '{genName}' not found", "Missing Method", patcher); return(null); } genTypes[i] = genType; } foreach (TypeDefinition type in genTypes) { generic.GenericArguments.Add(type); } methodMethod = generic; } Instruction = Instruction.Create(opcode, method.Module.Import(methodMethod)); break; case OpType.Generic: break; case OpType.Type: string[] typeData = Convert.ToString(instructionData.Operand).Split('|'); TypeReference typeType = GetType(typeData[0], TagsRegex.Replace(typeData[1], string.Empty).Trim(), patcher); if (typeType == null) { return(null); } start = typeData[1].IndexOf('['); end = typeData[1].IndexOf(']'); if (start >= 0 && end >= 0 && start < end) { GenericInstanceType generic = new GenericInstanceType(typeType); string typeG = typeData[1].Substring(start + 1, end - start - 1); string[] genData = typeG.Split(','); TypeDefinition[] genTypes = new TypeDefinition[genData.Length]; for (int i = 0; i < genData.Length; i++) { string s = genData[i]; string genName = s.Trim(); string assem = "mscorlib"; if (genName.Contains('|')) { string[] split = genName.Split('|'); assem = split[0].Trim(); genName = split[1].Trim(); } TypeDefinition genType = GetType(assem, genName, patcher); if (genType == null) { ShowMsg($"GenericType '{genName}' not found", "Missing Type", patcher); return(null); } genTypes[i] = genType; } foreach (TypeDefinition type in genTypes) { generic.GenericArguments.Add(type); } typeType = generic; } Instruction = Instruction.Create(opcode, method.Module.Import(typeType)); break; default: throw new ArgumentOutOfRangeException(); } return(Instruction); }
public bool Execute(TypeDefinition type, MethodDefinition methodDefinition, object parameterDefinitionOrFieldDefinition, CustomAttribute attribute, int instructionIndex) { TypeReference targetType = null; MethodDefinition selectedMethod = null; var parameterDefinition = parameterDefinitionOrFieldDefinition as ParameterDefinition; if (parameterDefinition != null) { targetType = parameterDefinition.ParameterType; } var fieldDefinition = parameterDefinitionOrFieldDefinition as FieldDefinition; if (fieldDefinition != null) { targetType = fieldDefinition.FieldType; } if (targetType != null) { try { SelectMethod(_argumentTypeDefinition, targetType, out selectedMethod); } catch (Exception ex) { var error = $"[{type.FullName}.{methodDefinition.Name}] {ex.Message}"; var sequencePoint = methodDefinition.GetFirstSequencePoint(); if (sequencePoint != null) { FodyEnvironment.WriteErrorPoint(error, sequencePoint); } else { FodyEnvironment.WriteError(error); } return(false); } } if (selectedMethod is null) { return(false); } var moduleDefinition = type.Module; var importedMethod = moduleDefinition.ImportReference(selectedMethod); var instructions = new List <Instruction>(); if (parameterDefinition != null) { BuildInstructions(moduleDefinition, type, methodDefinition, parameterDefinition, attribute, instructions); } if (fieldDefinition != null) { BuildInstructions(moduleDefinition, type, methodDefinition, fieldDefinition, attribute, instructions); } if (importedMethod.HasGenericParameters) { var genericInstanceMethod = new GenericInstanceMethod(importedMethod); genericInstanceMethod.GenericArguments.Add(targetType); instructions.Add(Instruction.Create(OpCodes.Call, genericInstanceMethod)); } else { instructions.Add(Instruction.Create(OpCodes.Call, importedMethod)); } methodDefinition.Body.Instructions.Insert(instructionIndex, instructions); return(true); }
MethodDefinition CreateDispatchRpcs(List <MethodDefinition> remoteMethods) { var method = new MethodDefinition("DispatchRpc", Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.Virtual, voidTypeReference); method.Parameters.Add(new ParameterDefinition("methodIdx", Mono.Cecil.ParameterAttributes.None, MainModule.TypeSystem.Byte)); method.Parameters.Add(new ParameterDefinition("bs", Mono.Cecil.ParameterAttributes.None, Import(bitReaderType))); method.Body.InitLocals = true; var il = method.Body.GetILProcessor(); var foo = new List <Instruction>(); // switch (methodIdx) for (int i = 0; i < remoteMethods.Count; ++i) { var boo = il.Create(OpCodes.Nop); foo.Add(boo); il.Emit(OpCodes.Ldarg_1); if (i == 0) { il.Emit(OpCodes.Brfalse_S, boo); } else { il.Emit(OpCodes.Ldc_I4_S, (sbyte)i); il.Emit(OpCodes.Beq_S, boo); } } var boo2 = il.Create(OpCodes.Nop); foo.Add(boo2); il.Emit(OpCodes.Br_S, boo2); // T argN = bs.Read...(); for (int i = 0; i < remoteMethods.Count; ++i) { var remoteMethod = remoteMethods[i]; il.Append(foo[i]); var moo = new List <byte>(); for (int j = 0; j < remoteMethod.Parameters.Count; ++j) { var param = remoteMethod.Parameters[j]; var typeDef = param.ParameterType.Resolve(); MethodReference result = null; bool isReplica = param.ParameterType.Name == "Replica"; bool isNetworkObject = TypeInheritsFrom(typeDef, "Cube.Replication.NetworkObject"); bool isSerializable = typeDef.Interfaces.Any(type => type.InterfaceType.FullName == typeof(Cube.Transport.IBitSerializable).FullName); if (isReplica) { result = bitStreamRead["ReplicaId"]; } else if (isNetworkObject) { result = bitStreamRead["T"]; } else if (isSerializable) { result = readSerializable; } else { if (typeDef.IsEnum) { typeDef = GetEnumUnderlyingType(typeDef).Resolve(); } result = bitStreamRead[typeDef.Name]; } moo.Add((byte)(method.Body.Variables.Count)); if (isReplica) { method.Body.Variables.Add(new VariableDefinition(Import(replicaType))); // Replica replica = base.ReplicaManager.GetReplica(bs.ReadReplicaId()); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, Import(replicaManagerProperty.GetMethod)); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, Import(result.Resolve())); il.Emit(OpCodes.Callvirt, Import(replicaManagerGetReplicaMethod)); il.Emit(OpCodes.Stloc, method.Body.Variables.Count - 1); } else if (isNetworkObject) { method.Body.Variables.Add(new VariableDefinition(Import(param.ParameterType.Resolve()))); // Replace generic arguments with some vodoo magic var mmm = new GenericInstanceMethod(readNetworkObject); mmm.GenericArguments.Add(Import(param.ParameterType.Resolve())); var mr = MainModule.ImportReference(mmm); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, mr); il.Emit(OpCodes.Stloc, method.Body.Variables.Count - 1); } else if (isSerializable) { method.Body.Variables.Add(new VariableDefinition(Import(param.ParameterType.Resolve()))); // default(DamageInfo).Deserialize(bs); il.Emit(OpCodes.Ldloca, method.Body.Variables.Count - 1); il.Emit(OpCodes.Initobj, param.ParameterType.Resolve()); il.Emit(OpCodes.Ldloca, method.Body.Variables.Count - 1); il.Emit(OpCodes.Ldarg_2); var deserialize = Import(GetMethodDefinitionByName(param.ParameterType.Resolve(), "Deserialize")); il.Emit(OpCodes.Call, deserialize); } else { method.Body.Variables.Add(new VariableDefinition(Import(param.ParameterType.Resolve()))); // var valN = bs.Read...(); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Callvirt, Import(result.Resolve())); il.Emit(OpCodes.Stloc, method.Body.Variables.Count - 1); } } // Rpc(arg0, arg1, ...); il.Emit(OpCodes.Ldarg_0); for (int j = 0; j < remoteMethod.Parameters.Count; ++j) { var idx = moo.Count - remoteMethod.Parameters.Count + j; il.Emit(OpCodes.Ldloc_S, moo[idx]); } il.Emit(OpCodes.Call, remoteMethod); il.Emit(OpCodes.Ret); } // Debug.LogError((object)"Missing RPC dispatch"); il.Append(foo[foo.Count - 1]); il.Emit(OpCodes.Ldstr, "Missing RPC dispatch"); il.Emit(OpCodes.Call, debugLogErrorMethod); il.Emit(OpCodes.Ret); return(method); }
/// <summary> /// Rewrites the specified <see cref="MethodReference"/>. /// </summary> /// <param name="method">The method reference to rewrite.</param> /// <param name="module">The module definition that is being visited.</param> /// <returns>The rewritten method, or the original if it was not changed.</returns> protected MethodReference RewriteMethodReference(MethodReference method, ModuleDefinition module) { MethodReference result = method; TypeReference declaringType = this.RewriteDeclaringTypeReference(method); if (method.DeclaringType == declaringType) { // We are not rewriting this method. return(result); } MethodDefinition resolvedMethod = Resolve(method); TypeDefinition resolvedDeclaringType = Resolve(declaringType); // This is an extra initial parameter that we have when converting an instance to a static method. // For example, `task.GetAwaiter()` is converted to `ControlledTask.GetAwaiter(task)`. ParameterDefinition instanceParameter = null; MethodDefinition match = FindMatchingMethodInDeclaringType(resolvedMethod, resolvedDeclaringType); if (match != null) { result = module.ImportReference(match); if (resolvedMethod.Parameters.Count != match.Parameters.Count) { // We are converting from an instance method to a static method, so store the instance parameter. instanceParameter = result.Parameters[0]; } } TypeReference returnType = this.RewriteTypeReference(method.ReturnType); // Instantiate the method reference to set its generic arguments and parameters, if any. result = new MethodReference(result.Name, returnType, declaringType) { HasThis = result.HasThis, ExplicitThis = result.ExplicitThis, CallingConvention = result.CallingConvention }; if (resolvedMethod.HasGenericParameters) { // We need to instantiate the generic method. GenericInstanceMethod genericMethodInstance = new GenericInstanceMethod(result); var genericArgs = new List <TypeReference>(); int genericArgOffset = 0; if (declaringType is GenericInstanceType genericDeclaringType) { // Populate the generic arguments with the generic declaring type arguments. genericArgs.AddRange(genericDeclaringType.GenericArguments); genericArgOffset = genericDeclaringType.GenericArguments.Count; } if (method is GenericInstanceMethod genericInstanceMethod) { // Populate the generic arguments with the generic instance method arguments. genericArgs.AddRange(genericInstanceMethod.GenericArguments); } for (int i = 0; i < resolvedMethod.GenericParameters.Count; i++) { var p = resolvedMethod.GenericParameters[i]; var j = p.Position + genericArgOffset; if (j > genericArgs.Count) { throw new InvalidOperationException(string.Format("Not enough generic arguments to instantiate method {0}", method)); } GenericParameter parameter = new GenericParameter(p.Name, genericMethodInstance); result.GenericParameters.Add(parameter); genericMethodInstance.GenericParameters.Add(parameter); genericMethodInstance.GenericArguments.Add(this.RewriteTypeReference(genericArgs[j])); } result = genericMethodInstance; } // Set the instance parameter of the method, if any. if (instanceParameter != null) { result.Parameters.Add(instanceParameter); } // Set the remaining parameters of the method, if any. foreach (var parameter in method.Parameters) { result.Parameters.Add(this.RewriteParameterDefinition(parameter)); } return(module.ImportReference(result)); }
private static TypeReference GetQueryCallExtent(Instruction queryInvocation) { GenericInstanceMethod method = (GenericInstanceMethod)queryInvocation.Operand; return(method.GenericArguments[0]); }
protected MethodDefinition GetOrCreateMethodProxy(MethodReference ifaceMethod) { var methodName = $"{ifaceMethod.DeclaringType.FullName}.{ifaceMethod.Name}"; var proxy = _target.Methods.FirstOrDefault(m => m.IsExplicitImplementationOf(ifaceMethod)); if (proxy == null) { proxy = new MethodDefinition(methodName, MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, ifaceMethod.ReturnType); _target.Methods.Add(proxy); var ifaceMethodDef = ifaceMethod.Resolve(); foreach (var gp in ifaceMethodDef.GenericParameters) { proxy.GenericParameters.Add(gp.Clone(proxy)); } TypeReference mayBeGeneric(TypeReference tr) => tr is GenericParameter gpr && gpr.Type == GenericParameterType.Method ? proxy.GenericParameters[gpr.Position] : tr; proxy.Mark(WellKnownTypes.DebuggerHiddenAttribute); //proxy.ReturnType = _target.Module.ImportReference(mayBeGeneric(ifaceMethod.ReturnType)); if (ifaceMethod.Resolve().IsSpecialName) { proxy.IsSpecialName = true; } foreach (var parameter in ifaceMethod.Parameters) { proxy.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, parameter.ParameterType)); } proxy.Overrides.Add(ifaceMethod); if (proxy.HasGenericParameters) { //var ifaceRef = ifaceMethod.Resolve().DeclaringType.MakeReference(); var gref = new GenericInstanceMethod(ifaceMethod); foreach (var gpar in proxy.GenericParameters) { gref.GenericArguments.Add(gpar); } ifaceMethod = gref; } proxy.Body.Instead( e => e .LoadAspect(_aspect) .Call(ifaceMethod, args => { foreach (var pp in proxy.Parameters) { args = args.Load(pp); } return(args); }) .Return() ); } return(proxy); }
/// <summary> /// [SyncVar] GameObject/NetworkIdentity? /// </summary> /// <param name="syncVar"></param> /// <param name="serWorker"></param> /// <param name="deserialize"></param> /// <param name="initialState"></param> /// <param name="hookResult"></param> void DeserializeNetworkIdentityField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize, MethodDefinition hookMethod) { /* * 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); * } */ // 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; VariableDefinition 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; VariableDefinition 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 GenericInstanceMethod 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 // Generates: OnValueChanged(oldValue, this.syncVar); SyncVarProcessor.WriteCallHookMethodUsingField(serWorker, hookMethod, oldSyncVar, syncVar); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } }
public static MethodDefinition ProcessSyncVarSet(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.voidType); ILProcessor worker = set.Body.GetILProcessor(); // if (!SyncVarEqual(value, ref playerData)) Instruction endOfMethod = worker.Create(OpCodes.Nop); // this worker.Append(worker.Create(OpCodes.Ldarg_0)); // new value to set worker.Append(worker.Create(OpCodes.Ldarg_1)); // reference to field to set // make generic version of SetSyncVar with field type if (fd.FieldType.FullName == WeaverTypes.gameObjectType.FullName) { // reference to netId Field to set worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldfld, netFieldId)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.syncVarGameObjectEqualReference)); } else if (fd.FieldType.FullName == WeaverTypes.NetworkIdentityType.FullName) { // reference to netId Field to set worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldfld, netFieldId)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.syncVarNetworkIdentityEqualReference)); } else { worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldflda, fd)); GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(WeaverTypes.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(fd.FieldType); worker.Append(worker.Create(OpCodes.Call, syncVarEqualGm)); } worker.Append(worker.Create(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.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldfld, fd)); worker.Append(worker.Create(OpCodes.Stloc, oldValue)); // this worker.Append(worker.Create(OpCodes.Ldarg_0)); // new value to set worker.Append(worker.Create(OpCodes.Ldarg_1)); // reference to field to set worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldflda, fd)); // dirty bit // 8 byte integer aka long worker.Append(worker.Create(OpCodes.Ldc_I8, dirtyBit)); if (fd.FieldType.FullName == WeaverTypes.gameObjectType.FullName) { // reference to netId Field to set worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldflda, netFieldId)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.setSyncVarGameObjectReference)); } else if (fd.FieldType.FullName == WeaverTypes.NetworkIdentityType.FullName) { // reference to netId Field to set worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldflda, netFieldId)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.setSyncVarNetworkIdentityReference)); } else { // make generic version of SetSyncVar with field type GenericInstanceMethod gm = new GenericInstanceMethod(WeaverTypes.setSyncVarReference); gm.GenericArguments.Add(fd.FieldType); // invoke SetSyncVar worker.Append(worker.Create(OpCodes.Call, gm)); } MethodDefinition hookMethod = GetHookMethod(td, fd); if (hookMethod != null) { //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit)) Instruction label = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.NetworkServerGetLocalClientActive)); worker.Append(worker.Create(OpCodes.Brfalse, label)); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldc_I8, dirtyBit)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.getSyncVarHookGuard)); worker.Append(worker.Create(OpCodes.Brtrue, label)); // setSyncVarHookGuard(dirtyBit, true); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldc_I8, dirtyBit)); worker.Append(worker.Create(OpCodes.Ldc_I4_1)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.setSyncVarHookGuard)); // call hook (oldValue, newValue) // Generates: OnValueChanged(oldValue, value); WriteCallHookMethodUsingArgument(worker, hookMethod, oldValue); // setSyncVarHookGuard(dirtyBit, false); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldc_I8, dirtyBit)); worker.Append(worker.Create(OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.setSyncVarHookGuard)); worker.Append(label); } worker.Append(endOfMethod); worker.Append(worker.Create(OpCodes.Ret)); set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); set.SemanticsAttributes = MethodSemanticsAttributes.Setter; return(set); }
/// <summary> /// [SyncVar] int/float/struct/etc.? /// </summary> /// <param name="syncVar"></param> /// <param name="serWorker"></param> /// <param name="deserialize"></param> /// <param name="initialState"></param> /// <param name="hookResult"></param> void DeserializeNormalField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize, MethodDefinition hookMethod) { /* * Generates code like: * // for hook * int oldValue = a; * Networka = reader.ReadPackedInt32(); * if (!SyncVarEqual(oldValue, ref a)) * { * OnSetA(oldValue, Networka); * } */ MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType); if (readFunc == null) { Weaver.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); return; } // T oldValue = value; VariableDefinition 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 GenericInstanceMethod 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 // Generates: OnValueChanged(oldValue, this.syncVar); SyncVarProcessor.WriteCallHookMethodUsingField(serWorker, hookMethod, oldValue, syncVar); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } }
/// <summary> /// Get method signature of invoked method. It will be resolved with /// generic arguments while the method is invoked. /// </summary> /// <param name="method">Method to get resolved signature.</param> /// <returns> /// Method signature with generic parameters resolved, e.g., /// List<string>.Add(string). /// Generic parameters are resolved to argument values. /// </returns> public static string GetResolvedMethodSignature(this MethodReference method) { string typeSignature = method.DeclaringType.GetResolvedTypeSignature(); string methodSignature; if (method.Name.StartsWith(GetPrefix)) { methodSignature = $"{method.Name.Substring(GetPrefix.Length)}.get"; } else if (method.Name.StartsWith(SetPrefix)) { methodSignature = $"{method.Name.Substring(SetPrefix.Length)}.set"; } else { methodSignature = method.Name; } if (method.IsGenericInstance) { GenericInstanceMethod genericInstance = (GenericInstanceMethod)method; var genericParameters = genericInstance.GenericArguments.Select(genericArgument => genericArgument.GetResolvedTypeSignature()); methodSignature = genericParameters.Count() != 0 ? $"{methodSignature}<{string.Join(",", genericParameters)}>" : methodSignature; } IEnumerable <string> parameters; if (method.IsGenericInstance) { GenericInstanceMethod genericInstanceMethod = (GenericInstanceMethod)method; parameters = method.Parameters.Select(parameter => { TypeReference resolvedParameterType = parameter.GetTypeWithGenericResolved(genericInstanceMethod); if (resolvedParameterType.IsByReference) { ByReferenceType byReference = resolvedParameterType as ByReferenceType; string prefix; if (parameter.IsOut) { prefix = "out"; } else if (parameter.IsIn) { prefix = "in"; } else { prefix = "ref"; } return($"{prefix}{byReference.ElementType.GetResolvedTypeSignature()}"); } if (parameter.IsParams()) { return($"params{resolvedParameterType.GetResolvedTypeSignature()}"); } return(resolvedParameterType.GetResolvedTypeSignature()); }); } else { parameters = method.Parameters.Select(parameter => { TypeReference resolvedParameterType = parameter.GetTypeWithGenericResolved(); if (resolvedParameterType.IsByReference) { ByReferenceType byReference = resolvedParameterType as ByReferenceType; string prefix; if (parameter.IsOut) { prefix = "out"; } else if (parameter.IsIn) { prefix = "in"; } else { prefix = "ref"; } return($"{prefix}{byReference.ElementType.GetResolvedTypeSignature()}"); } if (parameter.IsParams()) { return($"params{resolvedParameterType.GetResolvedTypeSignature()}"); } return(resolvedParameterType.GetResolvedTypeSignature()); }); } return($"{typeSignature}.{methodSignature}({string.Join(",", parameters)})"); }
public static void Patch(AssemblyDefinition ass) { var hardpatched = ass.MainModule.Resources.Any(r => string.Equals(r.Name, "ILRepack.List", StringComparison.InvariantCultureIgnoreCase)); if (hardpatched) { Console.WriteLine("Could not patch PHMoreSlotID because the assembly is already hardpatched. Restore the original Assembly-CSharp and try again."); return; } foreach (var subdir in new[] { @"abdata\list\accessory", @"abdata\list\custommaterial", @"abdata\list\customtexture", @"abdata\list\hair", @"abdata\list\wear", @"abdata\thumnbnail_R", }) { // Make sure the directories exist to mimic how the original was distributed, and to prevent crashes later on Directory.CreateDirectory(Path.Combine(Paths.GameRootPath, subdir)); } var hookAss = AssemblyDefinition.ReadAssembly(Path.Combine(BepInEx.Paths.PatcherPluginPath, "PHMoreSlotIDPatchContainer.dll")); var customDataSetupLoader = ass.MainModule.GetType("CustomDataSetupLoader`1") ?? throw new EntryPointNotFoundException("CustomDataSetupLoader`1"); var customDataSetupLoaderAction = customDataSetupLoader.Fields.FirstOrDefault(m => m.Name == "action") ?? throw new EntryPointNotFoundException("action"); { var targetMethod = customDataSetupLoader.Methods.FirstOrDefault(m => m.Name == "Setup") ?? throw new EntryPointNotFoundException("Setup"); var hookMethod = hookAss.MainModule.GetType("PHMoreSlotIDPatchContainer.CustomDataSetupLoader`1").Methods.FirstOrDefault(m => m.Name == "Setup"); var hookRef = ass.MainModule.ImportReference(hookMethod); var il = targetMethod.Body.GetILProcessor(); var ins = targetMethod.Body.Instructions.First(); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_1)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_2)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(ins, il.Create(OpCodes.Ldfld, customDataSetupLoaderAction)); il.InsertBefore(ins, il.Create(OpCodes.Call, hookRef)); il.InsertBefore(ins, il.Create(OpCodes.Ret)); } { var targetMethod = customDataSetupLoader.Methods.FirstOrDefault(m => m.Name == "Setup_Search") ?? throw new EntryPointNotFoundException("Setup_Search"); var hookMethod = hookAss.MainModule.GetType("PHMoreSlotIDPatchContainer.CustomDataSetupLoader`1").Methods.FirstOrDefault(m => m.Name == "Setup_Search"); var hookRef = ass.MainModule.ImportReference(hookMethod); var il = targetMethod.Body.GetILProcessor(); var ins = targetMethod.Body.Instructions.First(); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_1)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_2)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(ins, il.Create(OpCodes.Ldfld, customDataSetupLoaderAction)); il.InsertBefore(ins, il.Create(OpCodes.Call, hookRef)); il.InsertBefore(ins, il.Create(OpCodes.Ret)); } { var targetType = ass.MainModule.GetType("AssetBundleController"); var targetMethod = targetType.Methods.FirstOrDefault(m => m.Name == "LoadAsset") ?? throw new EntryPointNotFoundException("LoadAsset"); var hookMethod = hookAss.MainModule.GetType("PHMoreSlotIDPatchContainer.AssetBundleController").Methods.FirstOrDefault(m => m.Name == "LoadAsset"); var hookRef = ass.MainModule.ImportReference(hookMethod); var hookGenericRef = new GenericInstanceMethod(hookRef); hookGenericRef.GenericArguments.Add(targetMethod.GenericParameters[0]); var assetBundle = targetType.Fields.FirstOrDefault(m => m.Name == "assetBundle") ?? throw new EntryPointNotFoundException("assetBundle"); var directory = targetType.Properties.FirstOrDefault(m => m.Name == "directory")?.GetMethod ?? throw new EntryPointNotFoundException("directory"); var assetBundleName = targetType.Properties.FirstOrDefault(m => m.Name == "assetBundleName")?.GetMethod ?? throw new EntryPointNotFoundException("assetBundleName"); var il = targetMethod.Body.GetILProcessor(); var ins = targetMethod.Body.Instructions.First(); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_1)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(ins, il.Create(OpCodes.Ldfld, assetBundle)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(ins, il.Create(OpCodes.Callvirt, directory)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(ins, il.Create(OpCodes.Callvirt, assetBundleName)); il.InsertBefore(ins, il.Create(OpCodes.Call, hookGenericRef)); il.InsertBefore(ins, il.Create(OpCodes.Ret)); } { var targetType = ass.MainModule.GetType("ItemDataBase"); var targetMethod = targetType.Methods.FirstOrDefault(m => m.Name == ".ctor" && m.Parameters.Count == 5) ?? throw new EntryPointNotFoundException("ItemDataBase.ctor"); var hookMethod = hookAss.MainModule.GetType("PHMoreSlotIDPatchContainer.ItemDataBase").Methods.FirstOrDefault(m => m.Name == "CtorPostfix"); var hookRef = ass.MainModule.ImportReference(hookMethod); var id = targetType.Fields.FirstOrDefault(m => m.Name == "id") ?? throw new EntryPointNotFoundException("id"); var il = targetMethod.Body.GetILProcessor(); var ins = targetMethod.Body.Instructions.Last(); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(ins, il.Create(OpCodes.Ldflda, id)); il.InsertBefore(ins, il.Create(OpCodes.Ldarg_1)); il.InsertBefore(ins, il.Create(OpCodes.Call, hookRef)); } }
public static TypeReference GetTypeWithGenericResolved(this ParameterDefinition definition, GenericInstanceMethod genericInstanceMethod) { var typeWithGenericResolved = definition.ParameterType; bool isByReference = false; bool isArray = false; int arrayRank = 0; if (typeWithGenericResolved.IsByReference) { isByReference = true; ByReferenceType byReferenceType = (ByReferenceType)typeWithGenericResolved; typeWithGenericResolved = byReferenceType.ElementType; } if (typeWithGenericResolved.IsArray) { isArray = true; ArrayType arrayType = (ArrayType)typeWithGenericResolved; arrayRank = arrayType.Rank; typeWithGenericResolved = arrayType.ElementType; } if (typeWithGenericResolved.IsGenericInstance) { GenericInstanceType genericInstanceType = (GenericInstanceType)typeWithGenericResolved; typeWithGenericResolved = genericInstanceType.GetTypeWithGenericResolved(((MethodReference)definition.Method).DeclaringType as GenericInstanceType, genericInstanceMethod); } if (typeWithGenericResolved.IsGenericParameter) { GenericParameter genericParam = typeWithGenericResolved as GenericParameter; var declaringMethod = (MethodReference)definition.Method; if (genericParam.DeclaringType != null && declaringMethod.DeclaringType is GenericInstanceType genericInstanceTypeOfParam) { var position = genericInstanceTypeOfParam.ElementType.GenericParameters.GetIndexByName(typeWithGenericResolved.Name); if (position != -1) { typeWithGenericResolved = genericInstanceTypeOfParam.GenericArguments[position]; } } else if (genericParam.DeclaringMethod != null && genericInstanceMethod != null) { var position = declaringMethod.GetElementMethod().GenericParameters.GetIndexByName(typeWithGenericResolved.Name); if (position != -1) { typeWithGenericResolved = genericInstanceMethod.GenericArguments[position]; } } } if (isArray) { typeWithGenericResolved = new ArrayType(typeWithGenericResolved, arrayRank); } if (isByReference) { typeWithGenericResolved = new ByReferenceType(typeWithGenericResolved); } return(typeWithGenericResolved); }
public bool GenerateFor(TypeDefinition hookType, TypeDefinition hookILType, MethodDefinition method) { if (method.HasGenericParameters || method.IsAbstract || (method.IsSpecialName && !method.IsConstructor)) { return(false); } if (!HookOrig && method.Name.StartsWith("orig_")) { return(false); } if (!HookPrivate && method.IsPrivate) { return(false); } string suffix = null; if (method.Parameters.Count == 0) { suffix = ""; } IEnumerable <MethodDefinition> overloads = null; if (suffix == null) { overloads = method.DeclaringType.Methods.Where(other => !other.HasGenericParameters && other.Name == method.Name && other != method); if (overloads.Count() == 0) { suffix = ""; } } if (suffix == null) { StringBuilder builder = new StringBuilder(); for (int parami = 0; parami < method.Parameters.Count; parami++) { ParameterDefinition param = method.Parameters[parami]; if (!TypeNameMap.TryGetValue(param.ParameterType.FullName, out string typeName)) { typeName = GetFriendlyName(param.ParameterType, false); } if (overloads.Any(other => { ParameterDefinition otherParam = other.Parameters.ElementAtOrDefault(parami); return (otherParam != null && GetFriendlyName(otherParam.ParameterType, false) == typeName && otherParam.ParameterType.Namespace != param.ParameterType.Namespace); })) { typeName = GetFriendlyName(param.ParameterType, true); } builder.Append("_"); builder.Append(typeName.Replace(".", "").Replace("`", "")); } suffix = builder.ToString(); } string name = method.Name; if (name.StartsWith(".")) { name = name.Substring(1); } name = name + suffix; if (hookType.FindEvent(name) != null) { string nameTmp; for ( int i = 1; hookType.FindEvent(nameTmp = name + "_" + i) != null; i++ ) { ; } name = nameTmp; } // TODO: Fix possible conflict when other members with the same names exist. TypeDefinition delOrig = GenerateDelegateFor(method); delOrig.Name = "orig_" + name; delOrig.CustomAttributes.Add(GenerateEditorBrowsable(EditorBrowsableState.Never)); hookType.NestedTypes.Add(delOrig); TypeDefinition delHook = GenerateDelegateFor(method); delHook.Name = "hook_" + name; MethodDefinition delHookInvoke = delHook.FindMethod("Invoke"); delHookInvoke.Parameters.Insert(0, new ParameterDefinition("orig", ParameterAttributes.None, delOrig)); MethodDefinition delHookBeginInvoke = delHook.FindMethod("BeginInvoke"); delHookBeginInvoke.Parameters.Insert(0, new ParameterDefinition("orig", ParameterAttributes.None, delOrig)); delHook.CustomAttributes.Add(GenerateEditorBrowsable(EditorBrowsableState.Never)); hookType.NestedTypes.Add(delHook); ILProcessor il; GenericInstanceMethod endpointMethod; MethodReference methodRef = OutputModule.ImportReference(method); #region Hook MethodDefinition addHook = new MethodDefinition( "add_" + name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, OutputModule.TypeSystem.Void ); addHook.Parameters.Add(new ParameterDefinition(null, ParameterAttributes.None, delHook)); addHook.Body = new MethodBody(addHook); il = addHook.Body.GetILProcessor(); il.Emit(OpCodes.Ldtoken, methodRef); il.Emit(OpCodes.Call, m_GetMethodFromHandle); il.Emit(OpCodes.Ldarg_0); endpointMethod = new GenericInstanceMethod(m_Add); endpointMethod.GenericArguments.Add(delHook); il.Emit(OpCodes.Call, endpointMethod); il.Emit(OpCodes.Ret); hookType.Methods.Add(addHook); MethodDefinition removeHook = new MethodDefinition( "remove_" + name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, OutputModule.TypeSystem.Void ); removeHook.Parameters.Add(new ParameterDefinition(null, ParameterAttributes.None, delHook)); removeHook.Body = new MethodBody(removeHook); il = removeHook.Body.GetILProcessor(); il.Emit(OpCodes.Ldtoken, methodRef); il.Emit(OpCodes.Call, m_GetMethodFromHandle); il.Emit(OpCodes.Ldarg_0); endpointMethod = new GenericInstanceMethod(m_Remove); endpointMethod.GenericArguments.Add(delHook); il.Emit(OpCodes.Call, endpointMethod); il.Emit(OpCodes.Ret); hookType.Methods.Add(removeHook); EventDefinition evHook = new EventDefinition(name, EventAttributes.None, delHook) { AddMethod = addHook, RemoveMethod = removeHook }; hookType.Events.Add(evHook); #endregion #region Hook IL MethodDefinition addIL = new MethodDefinition( "add_" + name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, OutputModule.TypeSystem.Void ); addIL.Parameters.Add(new ParameterDefinition(null, ParameterAttributes.None, t_ILManipulator)); addIL.Body = new MethodBody(addIL); il = addIL.Body.GetILProcessor(); il.Emit(OpCodes.Ldtoken, methodRef); il.Emit(OpCodes.Call, m_GetMethodFromHandle); il.Emit(OpCodes.Ldarg_0); endpointMethod = new GenericInstanceMethod(m_Modify); endpointMethod.GenericArguments.Add(delHook); il.Emit(OpCodes.Call, endpointMethod); il.Emit(OpCodes.Ret); hookILType.Methods.Add(addIL); MethodDefinition removeIL = new MethodDefinition( "remove_" + name, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Static, OutputModule.TypeSystem.Void ); removeIL.Parameters.Add(new ParameterDefinition(null, ParameterAttributes.None, t_ILManipulator)); removeIL.Body = new MethodBody(removeIL); il = removeIL.Body.GetILProcessor(); il.Emit(OpCodes.Ldtoken, methodRef); il.Emit(OpCodes.Call, m_GetMethodFromHandle); il.Emit(OpCodes.Ldarg_0); endpointMethod = new GenericInstanceMethod(m_Unmodify); endpointMethod.GenericArguments.Add(delHook); il.Emit(OpCodes.Call, endpointMethod); il.Emit(OpCodes.Ret); hookILType.Methods.Add(removeIL); EventDefinition evIL = new EventDefinition(name, EventAttributes.None, t_ILManipulator) { AddMethod = addIL, RemoveMethod = removeIL }; hookILType.Events.Add(evIL); #endregion return(true); }
public void Inject() { var from = def.GetTypeRef(@params.FromClass); if (from == null) { from = self.GetTypeRef(@params.FromClass, true); } if (from == null) { Logging.DebugLogs("[{0}] Unable to find from type", GetType().Name); Logging.DebugLogs("\t{0} {1}", @params.FromClass, @params.ToClass); return; } var to = def.GetTypeRef(@params.ToClass); if (to == null) { to = self.GetTypeRef(@params.ToClass, true); } if (to == null) { Logging.DebugLogs("[{0}] Unable to find to type", GetType().Name); Logging.DebugLogs("\t{0} {1}", @params.FromClass, @params.ToClass); return; } var methods = def.Modules.SelectMany(m => m.Types).SelectMany(t => t.Methods).Where(m => m.HasBody).ToList(); foreach (var method in methods) { var body = method.Body; var instructions = body.Instructions; var processor = body.GetILProcessor(); for (int i = 0; i < instructions.Count; i++) { var ins = instructions[i]; if (ins.OpCode == OpCodes.Call || ins.OpCode == OpCodes.Callvirt) { var @ref = (MethodReference)ins.Operand; if (@ref.DeclaringType.FullName.Equals(@params.FromClass)) { var redirect = to.Resolve().Methods.Where(m => m.Name.Equals(@ref.Name) && (@ref.Parameters.Count + 1) == m.Parameters.Count).FirstOrDefault(); if (redirect != null) { var call = def.Import(redirect); if (@ref is GenericInstanceMethod) { GenericInstanceMethod genericCall = new GenericInstanceMethod(def.Import(redirect)); var gim = (GenericInstanceMethod)@ref; foreach (var arg in gim.GenericArguments) { genericCall.GenericArguments.Add(arg); } call = genericCall; } processor.Replace(ins, processor.Create(OpCodes.Call, call)); } } } } } }
private void ReplaceCalls(MethodBody body, Dictionary <TypeDefinition, TypeDefinition> replacements) { body.SimplifyMacros(); var calls = body.Instructions.Where(i => i.OpCode == OpCodes.Call); foreach (var call in calls) { var originalMethodReference = (MethodReference)call.Operand; var originalMethodDefinition = originalMethodReference.Resolve(); var declaringTypeReference = originalMethodReference.DeclaringType; var declaringTypeDefinition = declaringTypeReference.Resolve(); if (!originalMethodDefinition.IsStatic || !replacements.ContainsKey(declaringTypeDefinition)) { continue; } var replacementTypeReference = ModuleDefinition.ImportReference(replacements[declaringTypeDefinition]); if (declaringTypeReference.IsGenericInstance) { var declaringGenericType = (GenericInstanceType)declaringTypeReference; var genericType = new GenericInstanceType(replacementTypeReference); foreach (var arg in declaringGenericType.GenericArguments) { genericType.GenericArguments.Add(arg); } replacementTypeReference = ModuleDefinition.ImportReference(genericType); } var replacementMethod = replacementTypeReference.ReferenceMethod(originalMethodDefinition.Name); if (replacementMethod == null) { LogError($"Missing '{declaringTypeDefinition.FullName}.{originalMethodDefinition.Name}()' in '{replacementTypeReference.FullName}'"); continue; } if (!replacementMethod.Resolve().IsStatic) { LogError($"Replacement method '{replacementMethod.FullName}' is not static"); continue; } if (originalMethodReference.IsGenericInstance) { var originalGenericInstanceMethod = (GenericInstanceMethod)originalMethodReference; var genericInstanceMethod = new GenericInstanceMethod(replacementMethod); foreach (var arg in originalGenericInstanceMethod.GenericArguments) { genericInstanceMethod.GenericArguments.Add(arg); } call.Operand = ModuleDefinition.ImportReference(genericInstanceMethod); } else { call.Operand = replacementMethod; } } body.InitLocals = true; body.OptimizeMacros(); }
public void Add(GenericInstanceMethod genericInstanceMethod) { this.Add(TypeResolver.ElementTypeFor(genericInstanceMethod).FullName, genericInstanceMethod); }
Expression ConvertCall(InvocationExpression invocation) { if (invocation.Arguments.Count < 2) { return(NotSupported(invocation)); } Expression target; int firstArgumentPosition; Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(0)); if (m.Success) { target = null; firstArgumentPosition = 1; } else { m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1)); if (!m.Success) { return(NotSupported(invocation)); } target = invocation.Arguments.ElementAt(0); firstArgumentPosition = 2; } MethodReference mr = m.Get <AstNode>("method").Single().Annotation <MethodReference>(); if (mr == null) { return(null); } Expression convertedTarget; if (target == null || target is NullReferenceExpression) { // static method if (m.Has("declaringType")) { convertedTarget = new TypeReferenceExpression(m.Get <AstType>("declaringType").Single().Clone()); } else { convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType)); } } else { convertedTarget = Convert(target); if (convertedTarget == null) { return(null); } } MemberReferenceExpression mre = convertedTarget.Member(mr.Name); GenericInstanceMethod gim = mr as GenericInstanceMethod; if (gim != null) { foreach (TypeReference tr in gim.GenericArguments) { mre.TypeArguments.Add(AstBuilder.ConvertType(tr)); } } IList <Expression> arguments = null; if (invocation.Arguments.Count == firstArgumentPosition + 1) { Expression argumentArray = invocation.Arguments.ElementAt(firstArgumentPosition); arguments = ConvertExpressionsArray(argumentArray); } if (arguments == null) { arguments = new List <Expression>(); foreach (Expression argument in invocation.Arguments.Skip(firstArgumentPosition)) { Expression convertedArgument = Convert(argument); if (convertedArgument == null) { return(null); } arguments.Add(convertedArgument); } } MethodDefinition methodDef = mr.Resolve(); if (methodDef != null && methodDef.IsGetter) { PropertyDefinition indexer = AstMethodBodyBuilder.GetIndexer(methodDef); if (indexer != null) { return(new IndexerExpression(mre.Target.Detach(), arguments).WithAnnotation(indexer)); } } return(new InvocationExpression(mre, arguments).WithAnnotation(mr)); }
static private void Manage(this MethodDefinition method) { var _metadata = method.Metadata(); var _machine = method.CustomAttributes.SingleOrDefault(_Attribute => _Attribute.AttributeType.Resolve() == method.Module.Import(typeof(AsyncStateMachineAttribute)).Resolve()); var _authentic = method.Authentic(); var _intermediate = method.Intermediate(_authentic); method.Body = new MethodBody(method); for (var _index = 0; _index < _authentic.Parameters.Count; _index++) { switch (_index) { case 0: method.Body.Emit(OpCodes.Ldarg_0); break; case 1: method.Body.Emit(OpCodes.Ldarg_1); break; case 2: method.Body.Emit(OpCodes.Ldarg_2); break; case 3: method.Body.Emit(OpCodes.Ldarg_3); break; default: method.Body.Emit(OpCodes.Ldarg_S, method.Parameters[method.IsStatic ? _index : _index - 1]); break; } } if (method.GenericParameters.Count == 0) { method.Body.Emit(OpCodes.Ldsfld, _intermediate); method.Body.Emit(OpCodes.Calli, method.ReturnType, _authentic.Parameters); } else { var _type = new GenericInstanceType(_intermediate.DeclaringType); foreach (var _parameter in method.GenericParameters) { _type.GenericArguments.Add(_parameter); } method.Body.Emit(OpCodes.Ldsfld, new FieldReference(_intermediate.Name, _intermediate.FieldType, _type)); var _method = new GenericInstanceMethod(_authentic); foreach (var _parameter in method.GenericParameters) { _method.GenericArguments.Add(_parameter); } method.Body.Emit(OpCodes.Calli, _method.ReturnType, _method.Parameters); } method.Body.Emit(OpCodes.Ret); method.Body.OptimizeMacros(); if (_machine != null) { var _type = _machine.ConstructorArguments[0].Value as TypeDefinition; var _factory = _type.Field <Advice.Boundary.IFactory>("<Factory>", FieldAttributes.Public | FieldAttributes.Static); var _boundary = _type.Field <Advice.IBoundary>("<Boundary>", FieldAttributes.Public); _type.IsBeforeFieldInit = true; var _intializer = _type.Initializer(); _intializer.Body.Emit(OpCodes.Newobj, System.Reflection.Metadata.Constructor(() => new Advice.Boundary.Factory())); _intializer.Body.Emit(OpCodes.Stsfld, _factory.Relative()); _intializer.Body.Emit(OpCodes.Ret); var _constructor = _type.Methods.Single(m => m.IsConstructor && !m.IsStatic); _constructor.Body = new MethodBody(_constructor); _constructor.Body.Emit(OpCodes.Ldarg_0); _constructor.Body.Emit(OpCodes.Call, System.Reflection.Metadata.Constructor(() => new object())); _constructor.Body.Emit(OpCodes.Ldarg_0); _constructor.Body.Emit(OpCodes.Ldsfld, _constructor.Module.Import(_factory.Relative())); _constructor.Body.Emit(OpCodes.Callvirt, System.Reflection.Metadata <Advice.Boundary.IFactory> .Method(_Factory => _Factory.Create())); _constructor.Body.Emit(OpCodes.Stfld, _boundary.Relative()); _constructor.Body.Emit(OpCodes.Ret); var _move = _type.Methods.Single(_Method => _Method.Name == "MoveNext"); var _instance = method.IsStatic ? null : _type.Fields.Single(_Field => _Field.Name == "<>4__this").Relative(); var _state = _type.Fields.Single(_Field => _Field.Name == "<>1__state").Relative(); var _builder = _type.Fields.Single(_Field => _Field.Name == "<>t__builder").Relative(); var _offset = 0; var _begin = _move.Body.Instructions[_offset]; var _resume = Instruction.Create(OpCodes.Ldarg_0); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldarg_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldfld, _state)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldc_I4_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Bge, _resume)); if (_instance != null) { _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldarg_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldfld, _boundary.Relative())); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldarg_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldfld, _instance)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Instance <object>(System.Reflection.Argument <object> .Value)).GetGenericMethodDefinition()).MakeGenericMethod(method.DeclaringType)))); } foreach (var _parameter in method.Parameters) { _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldarg_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldfld, _boundary.Relative())); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldc_I4, _parameter.Index)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldarg_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldflda, _type.Fields.First(_Field => _Field.Name == _parameter.Name).Relative())); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Argument <object>(System.Reflection.Argument <int> .Value, ref System.Reflection.Argument <object> .Value)).GetGenericMethodDefinition()).MakeGenericMethod(_parameter.ParameterType.IsGenericParameter ? _type.GenericParameters.First(_Type => _Type.Name == _parameter.ParameterType.Name) : _parameter.ParameterType)))); } _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldarg_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldfld, _boundary.Relative())); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Callvirt, _move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Invoke())))); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Br_S, _begin)); _move.Body.Instructions.Insert(_offset++, _resume); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldfld, _boundary.Relative())); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Callvirt, _move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Resume())))); while (_offset < _move.Body.Instructions.Count) { var _instruction = _move.Body.Instructions[_offset]; if (_instruction.OpCode == OpCodes.Call) { if (_instruction.Operand is MethodReference) { var _operand = _instruction.Operand as MethodReference; if (_operand.Name == "AwaitUnsafeOnCompleted") { _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldarg_0)); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Ldfld, _boundary.Relative())); _move.Body.Instructions.Insert(_offset++, Instruction.Create(OpCodes.Callvirt, _move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Yield())))); } else if (_operand.Name == "SetResult") { var _return = _type.Method("<Return>", MethodAttributes.Public); if (_operand.HasParameters) { var _parameter = new ParameterDefinition("<Value>", ParameterAttributes.None, (_builder.FieldType as GenericInstanceType).GenericArguments[0]); _return.Parameters.Add(_parameter); var _exception = _return.Body.Variable <Exception>("<Exception>"); var _disposed = _return.Body.Variable <bool>("<Invoked>"); var _end = Instruction.Create(OpCodes.Ret); _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _return.Body.Emit(OpCodes.Ldarga_S, _parameter); _return.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Return <object>(ref System.Reflection.Argument <object> .Value)).GetGenericMethodDefinition()).MakeGenericMethod(_parameter.ParameterType))); _return.Body.Emit(OpCodes.Ldc_I4_1); _return.Body.Emit(OpCodes.Stloc_1); _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _return.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldflda, _builder); _return.Body.Emit(OpCodes.Ldarg_1); _return.Body.Emit(OpCodes.Call, _operand); _return.Body.Emit(OpCodes.Ret); var _catch = _return.Body.Emit(OpCodes.Stloc_0); _return.Body.Emit(OpCodes.Ldloc_1); using (_return.Body.False()) { _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _return.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); } _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldflda, _builder); _return.Body.Emit(OpCodes.Ldloc_0); var _method = _move.Module.Import(_builder.FieldType.Resolve().Methods.Single(_Method => _Method.Name == "SetException")); _method.DeclaringType = _builder.FieldType; _return.Body.Emit(OpCodes.Call, _method); _return.Body.Emit(OpCodes.Ret); _return.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch) { TryStart = _return.Body.Instructions[0], TryEnd = _return.Body.Instructions[_catch], HandlerStart = _return.Body.Instructions[_catch], HandlerEnd = _return.Body.Instructions[_return.Body.Instructions.Count - 1], CatchType = _exception.VariableType }); _return.Body.OptimizeMacros(); _instruction.Operand = _type.HasGenericParameters ? _return.MakeHostInstanceGeneric(_type.GenericParameters.ToArray()) : _return; _move.Body.Instructions[_offset - 2].OpCode = OpCodes.Nop; } else { var _exception = _return.Body.Variable <Exception>("<Exception>"); var _disposed = _return.Body.Variable <bool>("<Invoked>"); var _end = Instruction.Create(OpCodes.Ret); _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _return.Body.Emit(OpCodes.Callvirt, _move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Return()))); _return.Body.Emit(OpCodes.Ldc_I4_1); _return.Body.Emit(OpCodes.Stloc_1); _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _return.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldflda, _builder); _return.Body.Emit(OpCodes.Call, _operand); _return.Body.Emit(OpCodes.Ret); var _catch = _return.Body.Emit(OpCodes.Stloc_0); _return.Body.Emit(OpCodes.Ldloc_1); using (_return.Body.False()) { _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _return.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); } _return.Body.Emit(OpCodes.Ldarg_0); _return.Body.Emit(OpCodes.Ldflda, _builder); _return.Body.Emit(OpCodes.Ldloc_0); var _method = _move.Module.Import(_builder.FieldType.Resolve().Methods.Single(_Method => _Method.Name == "SetException")); _method.DeclaringType = _builder.FieldType; _return.Body.Emit(OpCodes.Call, _method); _return.Body.Emit(OpCodes.Ret); _return.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch) { TryStart = _return.Body.Instructions[0], TryEnd = _return.Body.Instructions[_catch], HandlerStart = _return.Body.Instructions[_catch], HandlerEnd = _return.Body.Instructions[_return.Body.Instructions.Count - 1], CatchType = _exception.VariableType, }); _return.Body.OptimizeMacros(); _instruction.Operand = _type.HasGenericParameters ? _return.MakeHostInstanceGeneric(_type.GenericParameters.ToArray()) : _return; _move.Body.Instructions[_offset - 1].OpCode = OpCodes.Nop; } } else if (_operand.Name == "SetException") { var _throw = _type.Method("<Throw>", MethodAttributes.Public); var _parameter = new ParameterDefinition("<Exception>", ParameterAttributes.None, _throw.Module.Import(typeof(Exception))); _throw.Parameters.Add(_parameter); if (_builder.FieldType.IsGenericInstance) { var _value = new VariableDefinition("<Value>", (_builder.FieldType as GenericInstanceType).GenericArguments[0]); _throw.Body.Variables.Add(_value); var _disposed = _throw.Body.Variable <bool>("<Invoked>"); _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _throw.Body.Emit(OpCodes.Ldarg_S, _parameter); _throw.Body.Emit(OpCodes.Ldloca_S, _value); _throw.Body.Emit(OpCodes.Callvirt, _move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Throw <object>(ref System.Reflection.Argument <Exception> .Value, ref System.Reflection.Argument <object> .Value)).GetGenericMethodDefinition()).MakeGenericMethod(_value.VariableType)); _throw.Body.Emit(OpCodes.Ldc_I4_1); _throw.Body.Emit(OpCodes.Stloc_1); _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _throw.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); _throw.Body.Emit(OpCodes.Ldarg_1); using (_throw.Body.True()) { _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldflda, _builder); _throw.Body.Emit(OpCodes.Ldarg_1); _throw.Body.Emit(OpCodes.Call, _operand); _throw.Body.Emit(OpCodes.Ret); } _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldflda, _builder); _throw.Body.Emit(OpCodes.Ldloc_0); var _method = _move.Module.Import(_move.Module.Import(_builder.FieldType.Resolve().Methods.Single(_Method => _Method.Name == "SetResult" && _Method.Parameters[0].ParameterType.IsGenericParameter))); _method.DeclaringType = _builder.FieldType; _throw.Body.Emit(OpCodes.Call, _method); _throw.Body.Emit(OpCodes.Ret); var _catch = _throw.Body.Emit(OpCodes.Starg, _parameter); _throw.Body.Emit(OpCodes.Ldloc_1); using (_throw.Body.False()) { _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _throw.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); } _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldflda, _builder); _throw.Body.Emit(OpCodes.Ldarg_1); _throw.Body.Emit(OpCodes.Call, _operand); _throw.Body.Emit(OpCodes.Ret); _throw.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch) { TryStart = _throw.Body.Instructions[0], TryEnd = _throw.Body.Instructions[_catch], HandlerStart = _throw.Body.Instructions[_catch], HandlerEnd = _throw.Body.Instructions[_throw.Body.Instructions.Count - 1], CatchType = _parameter.ParameterType, }); } else { var _disposed = _throw.Body.Variable <bool>("<Invoked>"); _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _throw.Body.Emit(OpCodes.Ldarga_S, _parameter); _throw.Body.Emit(OpCodes.Callvirt, _move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Throw(ref System.Reflection.Argument <Exception> .Value)))); _throw.Body.Emit(OpCodes.Ldc_I4_1); _throw.Body.Emit(OpCodes.Stloc_0); _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _throw.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); _throw.Body.Emit(OpCodes.Ldarg_1); using (_throw.Body.True()) { _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldflda, _builder); _throw.Body.Emit(OpCodes.Ldarg_1); _throw.Body.Emit(OpCodes.Call, _operand); _throw.Body.Emit(OpCodes.Ret); } _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldflda, _builder); var _method = _move.Module.Import(_builder.FieldType.Resolve().Methods.Single(_Method => _Method.Name == "SetResult" && _Method.Parameters.Count == 0)); _method.DeclaringType = _builder.FieldType; _throw.Body.Emit(OpCodes.Call, _method); _throw.Body.Emit(OpCodes.Ret); var _catch = _throw.Body.Emit(OpCodes.Starg, _parameter); _throw.Body.Emit(OpCodes.Ldloc_0); using (_throw.Body.False()) { _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldfld, _boundary.Relative()); _throw.Body.Emit(OpCodes.Callvirt, _move.Module.Import(_move.Module.Import(System.Reflection.Metadata <Advice.IBoundary> .Method(_Boundary => _Boundary.Dispose())))); } _throw.Body.Emit(OpCodes.Ldarg_0); _throw.Body.Emit(OpCodes.Ldflda, _builder); _throw.Body.Emit(OpCodes.Ldarg_1); _throw.Body.Emit(OpCodes.Call, _operand); _throw.Body.Emit(OpCodes.Ret); _throw.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch) { TryStart = _throw.Body.Instructions[0], TryEnd = _throw.Body.Instructions[_catch], HandlerStart = _throw.Body.Instructions[_catch], HandlerEnd = _throw.Body.Instructions[_throw.Body.Instructions.Count - 1], CatchType = _parameter.ParameterType, }); } _throw.Body.OptimizeMacros(); _instruction.Operand = _type.HasGenericParameters ? _throw.MakeHostInstanceGeneric(_type.GenericParameters.ToArray()) : _throw; _move.Body.Instructions[_offset - 2].OpCode = OpCodes.Nop; } } } _offset++; } _move.Body.OptimizeMacros(); } }
public static MethodDefinition ProcessSyncVarSet(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId) { //Create the set method var set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, Weaver.voidType); ILProcessor setWorker = set.Body.GetILProcessor(); // if (!SyncVarEqual(value, ref playerData)) Instruction endOfMethod = setWorker.Create(OpCodes.Nop); // this setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); // new value to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_1)); // reference to field to set // make generic version of SetSyncVar with field type if (fd.FieldType.FullName == Weaver.gameObjectType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldfld, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.syncVarGameObjectEqualReference)); } else if (fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldfld, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.syncVarNetworkIdentityEqualReference)); } else { setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, fd)); var syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(fd.FieldType); setWorker.Append(setWorker.Create(OpCodes.Call, syncVarEqualGm)); } setWorker.Append(setWorker.Create(OpCodes.Brtrue, endOfMethod)); // T oldValue = value; // TODO for GO/NI we need to backup the netId don't we? var oldValue = new VariableDefinition(fd.FieldType); set.Body.Variables.Add(oldValue); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldfld, fd)); setWorker.Append(setWorker.Create(OpCodes.Stloc, oldValue)); // this setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); // new value to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_1)); // reference to field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, fd)); // dirty bit // 8 byte integer aka long setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); if (fd.FieldType.FullName == Weaver.gameObjectType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarGameObjectReference)); } else if (fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarNetworkIdentityReference)); } else { // make generic version of SetSyncVar with field type var gm = new GenericInstanceMethod(Weaver.setSyncVarReference); gm.GenericArguments.Add(fd.FieldType); // invoke SetSyncVar setWorker.Append(setWorker.Create(OpCodes.Call, gm)); } MethodDefinition hookFunctionMethod = GetHookMethod(td, fd); if (hookFunctionMethod != null) { //if (base.isLocalClient && !getSyncVarHookGuard(dirtyBit)) Instruction label = setWorker.Create(OpCodes.Nop); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourIsLocalClient)); setWorker.Append(setWorker.Create(OpCodes.Brfalse, label)); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.getSyncVarHookGuard)); setWorker.Append(setWorker.Create(OpCodes.Brtrue, label)); // setSyncVarHookGuard(dirtyBit, true); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_1)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard)); // call hook (oldValue, newValue) // dont add this (Ldarg_0) if method is static if (!hookFunctionMethod.IsStatic) { setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); } setWorker.Append(setWorker.Create(OpCodes.Ldloc, oldValue)); setWorker.Append(setWorker.Create(OpCodes.Ldarg_1)); setWorker.Append(setWorker.Create(OpCodes.Callvirt, hookFunctionMethod)); // setSyncVarHookGuard(dirtyBit, false); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_0)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard)); setWorker.Append(label); } setWorker.Append(endOfMethod); setWorker.Append(setWorker.Create(OpCodes.Ret)); set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); set.SemanticsAttributes = MethodSemanticsAttributes.Setter; return(set); }
/// <summary> /// Inject the call of the injection method into the target. /// </summary> /// <param name="startCode">The instruction from which to start injecting.</param> /// <param name="token"> /// If <see cref="InjectFlags.PassTag" /> is specified, the value of this parameter will be passed as a /// parameter to the injection method. /// </param> /// <param name="direction">The direction in which to insert the call: either above the start code or below it.</param> public void Inject(Instruction startCode, int token = 0, InjectDirection direction = InjectDirection.Before) { InjectValues flags = Flags.ToValues(); #if DEBUG Logger.LogLine(LogMask.Inject, "##### INJECTION START #####"); Logger.LogLine( LogMask.Inject, $"Injecting a call to {InjectMethod.Module.Name}.{InjectMethod.Name} into {InjectTarget.Module.Name}.{InjectTarget.Name}."); Logger.LogLine(LogMask.Inject, "Patch parameters:"); Logger.LogLine(LogMask.Inject, $"Pass tag: {flags.PassTag}"); Logger.LogLine(LogMask.Inject, $"Modify return value: {flags.ModifyReturn}"); Logger.LogLine(LogMask.Inject, $"Pass THIS: {flags.PassInvokingInstance}"); Logger.LogLine(LogMask.Inject, $"Pass method locals: {flags.PassLocals}"); Logger.LogLine(LogMask.Inject, $"Pass member fields: {flags.PassFields}"); Logger.LogLine(LogMask.Inject, $"Pass member parameters: {flags.PassParameters}"); if (flags.PassParameters) { Logger.LogLine( LogMask.Inject, $"Member parameters are passed by {(flags.PassParametersByRef ? "reference" : "value")}"); } #endif bool isVoid = InjectTarget.ReturnType.FullName == "System.Void"; MethodReference hookRef = InjectTarget.Module.Import(InjectMethod); // If the hook is generic but not instantiated fully, attempt to fill in the generic arguments with the ones specified in the target method/class if (hookRef.HasGenericParameters && (!hookRef.IsGenericInstance || hookRef.IsGenericInstance && ((GenericInstanceMethod)hookRef).GenericArguments.Count < hookRef.GenericParameters.Count)) { GenericInstanceMethod genericInjectMethod = new GenericInstanceMethod(hookRef); foreach (GenericParameter genericParameter in InjectMethod.GenericParameters) { List<GenericParameter> @params = new List<GenericParameter>(); @params.AddRange(InjectTarget.GenericParameters); @params.AddRange(InjectTarget.DeclaringType.GenericParameters); GenericParameter param = @params.FirstOrDefault(p => p.Name == genericParameter.Name); if (param == null) throw new Exception("Could not find a suitable type to bind to the generic injection method. Try to manually instantiate the generic injection method before injecting."); genericInjectMethod.GenericArguments.Add(param); } hookRef = genericInjectMethod; } MethodBody targetBody = InjectTarget.Body; ILProcessor il = targetBody.GetILProcessor(); int startIndex = targetBody.Instructions.IndexOf(startCode); if (startIndex == -1) throw new ArgumentOutOfRangeException(nameof(startCode)); Instruction startInstruction = startCode; if (direction == InjectDirection.Before && startIndex != 0) { Instruction oldIns = ILUtils.CopyInstruction(startCode); ILUtils.ReplaceInstruction(startCode, il.Create(OpCodes.Nop)); Instruction ins = targetBody.Instructions[startIndex]; il.InsertAfter(ins, oldIns); startInstruction = targetBody.Instructions[startIndex + 1]; } else if (direction == InjectDirection.After) { il.InsertAfter(startCode, il.Create(OpCodes.Nop)); startInstruction = targetBody.Instructions[startIndex + 1]; } VariableDefinition returnDef = null; if (flags.ModifyReturn && !isVoid) { targetBody.InitLocals = true; returnDef = new VariableDefinition( InjectMethod.Name + "_return", InjectTarget.ReturnType); targetBody.Variables.Add(returnDef); } if (flags.PassTag) { Logger.LogLine(LogMask.Inject, $"Passing custom token value: {token}"); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldc_I4, token)); } if (flags.PassInvokingInstance) { Logger.LogLine(LogMask.Inject, "Passing THIS argument"); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0)); } if (flags.ModifyReturn && !isVoid) { Logger.LogLine(LogMask.Inject, "Passing return reference"); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloca_S, returnDef)); } if (flags.PassLocals) { Logger.LogLine(LogMask.Inject, "Passing local variable references"); foreach (int i in LocalVarIDs) { Logger.LogLine(LogMask.Inject, $"Passing local variable index: {i}"); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloca_S, (byte) i)); } } if (flags.PassFields) { Logger.LogLine(LogMask.Inject, "Passing member field references"); IEnumerable<FieldReference> memberRefs = MemberReferences.Select(t => t.Module.Import(t)); foreach (FieldReference t in memberRefs) { Logger.LogLine(LogMask.Inject, $"Passing member field {t.FullName}"); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0)); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldflda, t)); } } if (flags.PassParameters) { Logger.LogLine( LogMask.Inject, $"Passing member parameters by {(flags.PassParametersByRef ? "reference" : "value")}"); int icr = Convert.ToInt32(!InjectTarget.IsStatic); for (int i = 0; i < _ParameterCount; i++) { Logger.LogLine(LogMask.Inject, $"Passing parameter of index {(i + icr)}"); il.InsertBefore( startInstruction, flags.PassParametersByRef ? il.Create(OpCodes.Ldarga_S, (byte) (i + icr)) : il.Create(OpCodes.Ldarg_S, (byte) (i + icr))); } } Logger.LogLine(LogMask.Inject, "Injecting the call to the method"); il.InsertBefore(startInstruction, il.Create(OpCodes.Call, hookRef)); if (flags.ModifyReturn) { Logger.LogLine(LogMask.Inject, "Inserting return check"); il.InsertBefore(startInstruction, il.Create(OpCodes.Brfalse_S, startInstruction)); if (!isVoid) { Logger.LogLine(LogMask.Inject, "Inserting return value"); il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloc_S, returnDef)); } Logger.LogLine(LogMask.Inject, "Inserting return command"); il.InsertBefore(startInstruction, il.Create(OpCodes.Ret)); } // If we don't use the return value of InjectMethod, pop it from the ES else if (InjectMethod.ReturnType.FullName != "System.Void") il.InsertBefore(startInstruction, il.Create(OpCodes.Pop)); if (direction == InjectDirection.After) il.Remove(startInstruction); Logger.LogLine(LogMask.Inject, "Injection complete"); Logger.LogLine(LogMask.Inject, "##### INJECTION END #####"); }
/// <summary> /// Rewrites the specified <see cref="MethodReference"/>. /// </summary> /// <param name="method">The method reference to rewrite.</param> /// <param name="module">The module definition that is being visited.</param> /// <param name="matchName">Optional method name to match.</param> /// <returns>The rewritten method, or the original if it was not changed.</returns> protected MethodReference RewriteMethodReference(MethodReference method, ModuleDefinition module, string matchName = null) { MethodReference result = method; if (!this.TryResolve(method, out MethodDefinition resolvedMethod)) { // Can't rewrite external method reference since we are not rewriting this external assembly. return(method); } TypeReference declaringType = this.RewriteDeclaringTypeReference(method); var resolvedType = Resolve(declaringType); if (resolvedMethod == null) { // Check if this method signature has been rewritten, find the method by same name, // but with newly rewritten parameter types (note: signature does not include return type // according to C# rules, but the return type may have also been rewritten which is why // it is imperative here that we find the correct new MethodDefinition. List <TypeReference> parameterTypes = new List <TypeReference>(); for (int i = 0; i < method.Parameters.Count; i++) { var p = method.Parameters[i]; parameterTypes.Add(this.RewriteTypeReference(p.ParameterType)); } var newMethod = FindMatchingMethodInDeclaringType(resolvedType, method.Name, parameterTypes.ToArray()); if (newMethod != null) { if (!this.TryResolve(newMethod, out resolvedMethod)) { return(newMethod); } } } if (method.DeclaringType == declaringType && result.Resolve() == resolvedMethod) { // We are not rewriting this method. return(result); } if (resolvedMethod == null) { // print warning this.TryResolve(method, out resolvedMethod); // try and continue... return(method); } MethodDefinition match = FindMatchingMethodInDeclaringType(resolvedType, resolvedMethod, matchName); if (match != null) { result = module.ImportReference(match); } if (match != null && !result.HasThis && !declaringType.IsGenericInstance && method.HasThis && method.DeclaringType.IsGenericInstance) { // We are converting from a generic type to a non generic static type, and from a non-generic // method to a generic method, so we need to instantiate the generic method. GenericInstanceMethod genericMethodInstance = new GenericInstanceMethod(result); var genericArgs = new List <TypeReference>(); if (method.DeclaringType is GenericInstanceType genericDeclaringType) { // Populate the generic arguments with the generic declaring type arguments. genericArgs.AddRange(genericDeclaringType.GenericArguments); foreach (var genericArg in genericArgs) { genericMethodInstance.GenericArguments.Add(genericArg); } } result = genericMethodInstance; } else { // This is an extra initial parameter that we have when converting an instance to a static method. // For example, `task.GetAwaiter()` is converted to `ControlledTask.GetAwaiter(task)`. ParameterDefinition instanceParameter = null; if (match != null && resolvedMethod.Parameters.Count != match.Parameters.Count) { // We are converting from an instance method to a static method, so store the instance parameter. instanceParameter = result.Parameters[0]; } TypeReference returnType = this.RewriteTypeReference(method.ReturnType); // Instantiate the method reference to set its generic arguments and parameters, if any. result = new MethodReference(result.Name, returnType, declaringType) { HasThis = result.HasThis, ExplicitThis = result.ExplicitThis, CallingConvention = result.CallingConvention }; if (resolvedMethod.HasGenericParameters) { // We need to instantiate the generic method. GenericInstanceMethod genericMethodInstance = new GenericInstanceMethod(result); var genericArgs = new List <TypeReference>(); int genericArgOffset = 0; if (declaringType is GenericInstanceType genericDeclaringType) { // Populate the generic arguments with the generic declaring type arguments. genericArgs.AddRange(genericDeclaringType.GenericArguments); genericArgOffset = genericDeclaringType.GenericArguments.Count; } if (method is GenericInstanceMethod genericInstanceMethod) { // Populate the generic arguments with the generic instance method arguments. genericArgs.AddRange(genericInstanceMethod.GenericArguments); } for (int i = 0; i < resolvedMethod.GenericParameters.Count; i++) { var p = resolvedMethod.GenericParameters[i]; var j = p.Position + genericArgOffset; if (j > genericArgs.Count) { throw new InvalidOperationException($"Not enough generic arguments to instantiate method {method}"); } GenericParameter parameter = new GenericParameter(p.Name, genericMethodInstance); result.GenericParameters.Add(parameter); genericMethodInstance.GenericParameters.Add(parameter); genericMethodInstance.GenericArguments.Add(this.RewriteTypeReference(genericArgs[j])); } result = genericMethodInstance; } // Set the instance parameter of the method, if any. if (instanceParameter != null) { result.Parameters.Add(instanceParameter); } // Set the remaining parameters of the method, if any. foreach (var parameter in method.Parameters) { result.Parameters.Add(this.RewriteParameterDefinition(parameter)); } } return(module.ImportReference(result)); }