Beispiel #1
0
 private void RewriteType(TypeDefinition type, ObserverReferences observer)
 {
     foreach (MethodDefinition method in type.Methods)
     {
         RewriteMethod(method, observer);
     }
 }
Beispiel #2
0
        /// <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);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Adds a call to SpendGas from the RuntimeObserver before the given instruction.
        /// </summary>
        private static void AddSpendGasMethodBeforeInstruction(ILProcessor il, ObserverReferences observer, VariableDefinition variable, CodeSegment codeSegment)
        {
            Instruction first       = codeSegment.Instructions.First();
            Instruction newFirst    = il.CreateLdlocBest(variable);
            long        segmentCost = (long)codeSegment.CalculateGasCost().Value;

            il.Body.SimplifyMacros();
            il.InsertBefore(first, newFirst);                                         // load observer
            il.InsertBefore(first, il.Create(OpCodes.Ldc_I8, (long)segmentCost));     // load gas amount
            il.InsertBefore(first, il.Create(OpCodes.Call, observer.SpendGasMethod)); // trigger method
            il.Body.OptimizeMacros();
        }
Beispiel #4
0
        public ModuleDefinition Rewrite(ModuleDefinition module)
        {
            Guid id = Guid.NewGuid();

            FieldDefinition observerInstanceField = GetObserverInstance(module, id);
            var             observer = new ObserverReferences(observerInstanceField, module);

            foreach (TypeDefinition type in module.Types)
            {
                RewriteType(type, observer);
            }

            ObserverInstances.Set(id, this.observerToInject);

            return(module);
        }
        /// <summary>
        /// Adds a call to SpendGas from the RuntimeObserver before the given instruction.
        /// </summary>
        private static void AddSpendGasMethodBeforeInstruction(MethodDefinition methodDefinition, ObserverReferences observer, VariableDefinition variable, Instruction instruction, Gas opcodeCount)
        {
            ILProcessor il = methodDefinition.Body.GetILProcessor();

            il.Body.SimplifyMacros();
            il.InsertBefore(instruction, il.CreateLdlocBest(variable));                       // load observer
            il.InsertBefore(instruction, il.Create(OpCodes.Ldc_I8, (long)opcodeCount.Value)); // load gas amount
            il.InsertBefore(instruction, il.Create(OpCodes.Call, observer.SpendGasMethod));   // trigger method
            il.Body.OptimizeMacros();
        }
Beispiel #6
0
        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
            }
            List <Instruction> branches  = methodDefinition.Body.Instructions.Where(x => BranchingOps.Contains(x.OpCode)).ToList();
            List <Instruction> branchTos = branches.Select(x => (Instruction)x.Operand).ToList();

            Instruction currentSegmentStart = methodDefinition.Body.Instructions.FirstOrDefault();
            Gas         gasTally            = Gas.None;

            Dictionary <Instruction, Gas> gasToSpendForSegment = new Dictionary <Instruction, Gas>();

            // 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));

            // Start at 2 because of the instructions we just added.
            int position = 2;

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

                Gas instructionCost = GasPriceList.InstructionOperationCost(instruction);

                // is the end of a segment. Include the current instruction in the count.
                if (branches.Contains(instruction))
                {
                    gasTally = (Gas)(gasTally + instructionCost);
                    gasToSpendForSegment.Add(currentSegmentStart, gasTally);
                    gasTally = Gas.None;
                    position++;
                    if (position == methodDefinition.Body.Instructions.Count)
                    {
                        break;
                    }
                    currentSegmentStart = methodDefinition.Body.Instructions[position];
                }
                // is the start of a new segment. Don't include the current instruction in count.
                else if (branchTos.Contains(instruction) && instruction != currentSegmentStart)
                {
                    gasToSpendForSegment.Add(currentSegmentStart, gasTally);
                    gasTally            = Gas.None;
                    currentSegmentStart = instruction;
                    position++;
                }
                // is a call to another method
                else if (CallingOps.Contains(instruction.OpCode))
                {
                    var methodToCall = (MethodReference)instruction.Operand;

                    // If it's a method inside this contract then the gas will be injected no worries.
                    if (methodToCall.DeclaringType == methodDefinition.DeclaringType)
                    {
                        position++;
                        gasTally = (Gas)(gasTally + instructionCost);
                    }
                    // If it's a method outside this contract then we will need to get some average in future.
                    else
                    {
                        Gas methodCallCost = GasPriceList.MethodCallCost(methodToCall);

                        position++;
                        gasTally = (Gas)(gasTally + instructionCost + methodCallCost);
                    }
                }
                // any other instruction. just increase counter.
                else
                {
                    position++;
                    gasTally = (Gas)(gasTally + instructionCost);
                }
            }

            if (!gasToSpendForSegment.ContainsKey(currentSegmentStart))
            {
                gasToSpendForSegment.Add(currentSegmentStart, gasTally);
            }

            foreach (Instruction instruction in gasToSpendForSegment.Keys)
            {
                Instruction injectAfterInstruction = instruction;

                // If it's a constructor we need to skip the first 3 instructions.
                // These will always be invoking the base constructor
                // ldarg.0
                // ldarg.0
                // call SmartContract::ctor
                if (methodDefinition.IsConstructor)
                {
                    injectAfterInstruction = instruction.Next.Next.Next;
                }

                AddSpendGasMethodBeforeInstruction(methodDefinition, observer, observerVariable, injectAfterInstruction, gasToSpendForSegment[instruction]);
            }

            foreach (Instruction instruction in branches)
            {
                var         oldReference   = (Instruction)instruction.Operand;
                Instruction newReference   = oldReference.Previous.Previous.Previous; // 3 were inserted
                Instruction newInstruction = il.Create(instruction.OpCode, newReference);
                il.Replace(instruction, newInstruction);
            }
        }
 public ObserverRewriterContext(ObserverReferences observer, VariableDefinition observerVariable)
 {
     this.Observer         = observer;
     this.ObserverVariable = observerVariable;
 }