public static bool ParseMMILBatchAccessCtorCall(this MonoModder self, MethodBody body, MethodReference callCtor, ref int instri) { ILProcessor il = body.GetILProcessor(); Collection <Instruction> instrs = body.Instructions; TypeReference type = null; IMetadataTokenProvider member = null; ParseMMILAccessCtorHead(self, body, callCtor, ref instri, out type, out member); TypeDefinition typeDef = type.Resolve(); List <string> with = new List <string>(); List <string> without = new List <string>(); List <string> current = new List <string>(); // Currently in front of us: /* * ld arrsize (opt) * newarr (opt) * arr element #0 (opt) * arr element #1 (opt) * arr element #n (opt) * call With / Without / ... (opt) * target (opt) * call CopyTo / get_AllMethods / ... */ int?count = instrs[instri].GetIntOrNull(); if (count != null) { ParseFilter: instrs.RemoveAt(instri); // Remove the newarr instrs.RemoveAt(instri); // Parse array content for (int i = 0; i < count; i++) { instrs.RemoveAt(instri); // arr instrs.RemoveAt(instri); // index // ldstr current.Add((string)instrs[instri].Operand); instrs.RemoveAt(instri); instrs.RemoveAt(instri); // stelem.ref } // Should be at With / Without now MethodReference callFilter = (MethodReference)instrs[instri].Operand; if (callFilter.Name == "With") { with.AddRange(current); } else if (callFilter.Name == "Without") { without.AddRange(current); } current.Clear(); instrs.RemoveAt(instri); // call // Follow-up. if ((count = instrs[instri].GetIntOrNull()) != null) { goto ParseFilter; } } // Skip any target-loading instructions while (!((instrs[instri].Operand as MethodReference)?.DeclaringType?.Name?.StartsWith("BatchAccess") ?? false)) { // Nested parsing self.DefaultParser(self, body, instrs[instri], ref instri); instri++; } // FINALLY replace the call as required Instruction callInstr = instrs[instri]; MethodDefinition call = ((MethodReference)callInstr.Operand).Resolve(); VariableDefinition varTarget = null; if (call.Parameters.Count != 0 && call.Parameters[call.Parameters.Count - 1].Name == "target") { // Replace MMILBatchAccess call with local store for the target varTarget = new VariableDefinition(type); body.Variables.Add(varTarget); instrs[instri] = il.Create(OpCodes.Stloc, varTarget.Index); instri++; } else { // Remove MMILBatchAccess call instrs.RemoveAt(instri); } switch (call.Name) { case "get_AllMethods": il.InsertMetadataTokenArray(ref instri, typeDef.Methods.Filtered(with, without)); break; case "get_AllFields": il.InsertMetadataTokenArray(ref instri, typeDef.Fields.Filtered(with, without)); break; case "get_AllProperties": il.InsertMetadataTokenArray(ref instri, typeDef.Properties.Filtered(with, without)); break; case "CopyTo": Collection <IMetadataTokenProvider> tokens = new Collection <IMetadataTokenProvider>(); for (int i = 0; i < typeDef.Fields.Count; i++) { FieldDefinition mtp = typeDef.Fields[i]; if (mtp.IsStatic) { continue; } string name = mtp.Name; string nameExplicit = "field:" + name; if (without.Count != 0 && (without.Contains(name) || without.Contains(nameExplicit))) { continue; } if (with.Count == 0 && !mtp.IsPublic) { continue; } if (with.Count == 0 || with.Contains(name) || with.Contains(nameExplicit)) { tokens.Add(mtp); } } for (int i = 0; i < typeDef.Properties.Count; i++) { PropertyDefinition mtp = typeDef.Properties[i]; if (!mtp.HasThis) { continue; } if (mtp.GetMethod == null || mtp.SetMethod == null) { continue; } string name = mtp.Name; string nameExplicit = "property:" + name; if (without.Count != 0 && (without.Contains(name) || without.Contains(nameExplicit))) { continue; } if (with.Count == 0 && (!mtp.GetMethod.IsPublic || !mtp.SetMethod.IsPublic)) { continue; } if (with.Count == 0 || with.Contains(name) || with.Contains(nameExplicit)) { tokens.Add(mtp); } } // Store source in local VariableDefinition varSource = new VariableDefinition(type); body.Variables.Add(varSource); instrs[instri] = il.Create(OpCodes.Stloc, varSource.Index); instri++; il.InsertCopyTo(ref instri, tokens, varSource, varTarget); break; } instri--; return(false); // Don't let the PatchRefs pass handle the newly emitted call! }
public static bool ParseMMILAccessCtorCall(MonoModder self, MethodBody body, MethodReference callCtor, ref int instri) { ILProcessor il = body.GetILProcessor(); Collection <Instruction> instrs = body.Instructions; bool staticAccess = callCtor.DeclaringType.Name == "StaticAccess" || callCtor.DeclaringType.Name == "StaticAccess`1"; TypeReference type = null; IMetadataTokenProvider member = null; ParseMMILAccessCtorHead(self, body, callCtor, ref instri, out type, out member); if (instrs[instri + 1].OpCode == OpCodes.Newarr) { // Currently in front of us: /* * ld arrsize * newarr * arr element #0 * arr element #1 * arr element #n * call New / Call / Get / Set */ int count = instrs[instri].GetInt(); instrs.RemoveAt(instri); // Remove the newarr instrs.RemoveAt(instri); // Parse array content int depth = 0; Instruction instr = null; // Skip anything including nested arrays for (int i = 0; i < count; i++) { instrs.RemoveAt(instri); // arr instrs.RemoveAt(instri); // index while ((instr = instrs[instri]).OpCode != OpCodes.Stelem_Ref || depth > 0) { // Nested parsing self.DefaultParser(self, body, instrs[instri], ref instri); if (instr.OpCode == OpCodes.Newarr) { depth++; } else if (depth > 0 && instr.OpCode == OpCodes.Stelem_Ref) { depth--; } instri++; } // At Stelem_Ref right now if (instrs[instri - 1].OpCode == OpCodes.Box) { instrs.RemoveAt(instri - 1); instri--; } instrs.RemoveAt(instri); // stelem.ref } } else { // Currently in front of us: /* * ANYTHING * call New / Call / Get / Set */ for (Instruction instr = instrs[instri]; instri < instrs.Count && !( (instr.OpCode == OpCodes.Call || instr.OpCode == OpCodes.Callvirt) && (instr.Operand as MethodReference).DeclaringType.Namespace == "MMILAccess" ); instr = instrs[++instri]) { instr = instrs[instri]; // Nested parsing self.DefaultParser(self, body, instrs[instri], ref instri); } } // FINALLY replace the call as required Instruction callInstr = instrs[instri]; MethodDefinition call = ((MethodReference)callInstr.Operand).Resolve(); // Remove the MMILAccess call instrs.RemoveAt(instri); if (staticAccess) { switch (call.Name) { case "New": instrs.Insert(instri, il.Create(OpCodes.Newobj, (MethodReference)member)); instri++; break; case "Call": instrs.Insert(instri, il.Create(OpCodes.Call, (MethodReference)member)); instri++; il.InsertLdnullIfRequired(ref instri, (MethodReference)member); break; case "Get": if (member is FieldReference) { instrs.Insert(instri, il.Create(OpCodes.Ldsfld, (FieldReference)member)); } else { instrs.Insert(instri, il.Create(OpCodes.Call, (MethodReference)member)); } instri++; break; case "Set": if (member is FieldReference) { instrs.Insert(instri, il.Create(OpCodes.Stsfld, (FieldReference)member)); } else { instrs.Insert(instri, il.Create(OpCodes.Call, (MethodReference)member)); } instri++; break; } } else { switch (call.Name) { case "Call": instrs.Insert(instri, il.Create(OpCodes.Callvirt, (MethodReference)member)); instri++; il.InsertLdnullIfRequired(ref instri, (MethodReference)member); break; case "Get": if (member is FieldReference) { instrs.Insert(instri, il.Create(OpCodes.Ldfld, (FieldReference)member)); } else { instrs.Insert(instri, il.Create(OpCodes.Callvirt, (MethodReference)member)); } instri++; break; case "Set": if (member is FieldReference) { instrs.Insert(instri, il.Create(OpCodes.Stfld, (FieldReference)member)); } else { instrs.Insert(instri, il.Create(OpCodes.Callvirt, (MethodReference)member)); } instri++; break; } } instri--; return(false); // Don't let the PatchRefs pass handle the newly emitted call! }