Example #1
0
        public void Rewrite(MethodDefinition methodDefinition, ILProcessor il, ObserverRewriterContext context)
        {
            // Start from 2 - we added the 2 load variable instructions in ObserverRewriter
            for (int i = 2; i < methodDefinition.Body.Instructions.Count; i++)
            {
                // Keep current number of instructions to detect changes.
                int instructionCount = methodDefinition.Body.Instructions.Count;

                Instruction instruction = methodDefinition.Body.Instructions[i];

                if (instruction.OpCode.Code == Code.Newarr)
                {
                    this.CheckArrayCreationSize(instruction, il, context);
                }

                if (instruction.Operand is MethodReference called)
                {
                    this.PossiblyRewriteCalledMethod(called, instruction, il, context);
                }

                // If we rewrote, need to increase our iterator by the number of instructions we inserted.
                int instructionsAdded = methodDefinition.Body.Instructions.Count - instructionCount;
                i += instructionsAdded;
            }
        }
Example #2
0
 /// <summary>
 /// Insert instructions to check on the size of an array before it is created.
 /// Assumes the item on top of the stack at the moment is an int for the length of the array we're creating.
 /// </summary>
 private void CheckArrayCreationSize(Instruction instruction, ILProcessor il, ObserverRewriterContext context)
 {
     il.Body.SimplifyMacros();
     il.InsertBefore(instruction, il.CreateLdlocBest(context.ObserverVariable));
     il.InsertBefore(instruction, il.Create(OpCodes.Call, context.Observer.FlowThroughMemoryInt32Method));
     il.Body.OptimizeMacros();
 }
        /// <inheritdoc />
        public void Rewrite(MethodDefinition methodDefinition, ILProcessor il, ObserverRewriterContext context)
        {
            List <Instruction> branches  = GetBranchingOps(methodDefinition).ToList();
            List <Instruction> branchTos = branches.Select(x => (Instruction)x.Operand).ToList();

            // Start from 2 because we setup Observer in first 2 instructions
            int position = 2;

            List <CodeSegment> segments = new List <CodeSegment>();

            var codeSegment = new CodeSegment(methodDefinition);

            while (position < methodDefinition.Body.Instructions.Count)
            {
                Instruction instruction = methodDefinition.Body.Instructions[position];

                // End of a segment. Add this as the last instruction and move onwards with a new segment
                if (branches.Contains(instruction))
                {
                    codeSegment.Instructions.Add(instruction);
                    segments.Add(codeSegment);
                    codeSegment = new CodeSegment(methodDefinition);
                }
                // Start of a new segment. End last segment and start new one with this as the first instruction
                else if (branchTos.Contains(instruction))
                {
                    if (codeSegment.Instructions.Any())
                    {
                        segments.Add(codeSegment);
                    }
                    codeSegment = new CodeSegment(methodDefinition);
                    codeSegment.Instructions.Add(instruction);
                }
                // Just an in-between instruction. Add to current segment.
                else
                {
                    codeSegment.Instructions.Add(instruction);
                }

                position++;
            }

            // Got to end of the method. Add the last one if necessary
            if (!segments.Contains(codeSegment) && codeSegment.Instructions.Any())
            {
                segments.Add(codeSegment);
            }

            foreach (CodeSegment segment in segments)
            {
                AddSpendGasMethodBeforeInstruction(il, context.Observer, context.ObserverVariable, segment);
            }

            // All of the branches now need to point to the place 3 instructions earlier!
            foreach (Instruction branch in branches)
            {
                Instruction currentlyPointingTo = (Instruction)branch.Operand;
                branch.Operand = currentlyPointingTo.Previous.Previous.Previous;
            }
        }
        /// <summary>
        /// Makes the <see cref="Observer"/> available to the given method as a variable and then
        /// applies all of the individual rewriters to the method.
        /// </summary>
        private void RewriteMethod(MethodDefinition methodDefinition, ObserverReferences observer)
        {
            if (methodDefinition.DeclaringType == observer.InstanceField.DeclaringType)
            {
                return; // don't inject on our injected type.
            }
            if (!methodDefinition.HasBody || methodDefinition.Body.Instructions.Count == 0)
            {
                return; // don't inject on method without a Body
            }
            // Inject observer instance to method.
            ILProcessor il = methodDefinition.Body.GetILProcessor();
            var         observerVariable = new VariableDefinition(observer.InstanceField.FieldType);

            il.Body.Variables.Add(observerVariable);
            Instruction start = methodDefinition.Body.Instructions[0];

            il.InsertBefore(start, il.Create(OpCodes.Ldsfld, observer.InstanceField));
            il.InsertBefore(start, il.CreateStlocBest(observerVariable));

            var context = new ObserverRewriterContext(observer, observerVariable);

            foreach (IObserverMethodRewriter rewriter in methodRewriters)
            {
                rewriter.Rewrite(methodDefinition, il, context);
            }
        }
Example #5
0
        /// <summary>
        /// If is the specific string constructor that takes in a 'count' parameter, we need to check its size,
        /// </summary>
        private void CheckStringConstructor(Instruction instruction, ILProcessor il, ObserverRewriterContext context)
        {
            MethodDefinition method = ((MethodReference)instruction.Operand).Resolve();

            // Ensure is the constructor with a count param (not all string constructors have a count param)
            if (method.Parameters.Any(x => x.Name == "count"))
            {
                this.CheckArrayCreationSize(instruction, il, context);
            }
        }
Example #6
0
 /// <summary>
 /// Insert instructions to check on the size of an array that has just been pushed onto the top of the stack.
 /// </summary>
 private void CheckArrayReturnSize(Instruction instruction, ILProcessor il, ObserverRewriterContext context)
 {
     // TODO: We could do away with the pop on the end and not return anything from the observer method but we would need to cast to long correctly.
     il.Body.SimplifyMacros();
     il.InsertAfter(instruction,
                    il.Create(OpCodes.Dup),
                    il.Create(OpCodes.Ldlen),
                    il.CreateLdlocBest(context.ObserverVariable),
                    il.Create(OpCodes.Call, context.Observer.FlowThroughMemoryInt32Method),
                    il.Create(OpCodes.Pop)
                    );
     il.Body.OptimizeMacros();
 }
Example #7
0
        /// <summary>
        /// Checks if it is one of a few method calls we need to check on.
        /// If so, does the necessary IL rewrite.
        /// </summary>
        private void PossiblyRewriteCalledMethod(MethodReference called, Instruction instruction, ILProcessor il, ObserverRewriterContext context)
        {
            if (called.DeclaringType.FullName == typeof(Array).FullName && called.Name == nameof(Array.Resize))
            {
                this.CheckArrayCreationSize(instruction, il, context);
                return;
            }

            if (called.DeclaringType.FullName == typeof(string).FullName)
            {
                if (called.Name == nameof(string.ToCharArray))
                {
                    this.CheckArrayReturnSize(instruction, il, context);
                }
                else if (called.Name == nameof(string.Split))
                {
                    this.CheckArrayReturnSize(instruction, il, context);
                }
                else if (called.Name == nameof(string.Concat))
                {
                    this.CheckArrayReturnSize(instruction, il, context);
                }
                else if (called.Name == nameof(string.Join))
                {
                    this.CheckArrayReturnSize(instruction, il, context);
                }
                else if (called.Name == ".ctor")
                {
                    this.CheckStringConstructor(instruction, il, context);
                }
            }
        }