Exemplo n.º 1
0
        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!
        }
Exemplo n.º 2
0
        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!
        }