private ExceptionHandler BeginHandler(HandlerKind kind) //^ requires InTryBody; { ILGeneratorLabel handlerStart = new ILGeneratorLabel(false); this.MarkLabel(handlerStart); TryBody currentTryBody = this.tryBodyStack.Peek(); ExceptionHandler handler = new ExceptionHandler(kind, currentTryBody, handlerStart); if (currentTryBody.end == null) { currentTryBody.end = handlerStart; } else if (this.handlers.Count > 0) { for (int i = this.handlers.Count - 1; i >= 0; i--) { if (this.handlers[i].HandlerEnd == null) { this.handlers[i].HandlerEnd = handlerStart; break; } } } this.handlers.Add(handler); return(handler); }
internal ExceptionHandler(HandlerKind kind, TryBody tryBlock, ILGeneratorLabel handlerStart) { this.ExceptionType = Dummy.TypeReference; this.kind = kind; this.HandlerStart = handlerStart; this.tryBlock = tryBlock; }
/// <summary> /// Begins the body of a try statement. /// </summary> public void BeginTryBody() //^ ensures InTryBody; { ILGeneratorLabel tryBodyStart = new ILGeneratorLabel(false); this.MarkLabel(tryBodyStart); this.tryBodyStack.Push(new TryBody(tryBodyStart)); }
/// <summary> /// Begins the part of a filter handler that is invoked on the second pass if the filter condition returns true on the first pass. /// </summary> public void BeginFilterBody() { this.Emit(OperationCode.Endfilter); ILGeneratorLabel handlerStart = new ILGeneratorLabel(false); this.MarkLabel(handlerStart); this.handlers[handlers.Count - 1].HandlerStart = handlerStart; }
/// <summary> /// Returns a label that represents the given offset in the original IL. This label must be marked /// at the corresponding location in the rewritten IL. /// </summary> protected virtual ILGeneratorLabel GetLabelFor(uint offset) { Contract.Ensures(Contract.Result <ILGeneratorLabel>() != null); var result = this.labelFor[offset]; if (result == null) { this.labelFor[offset] = result = new ILGeneratorLabel(); } return(result); }
/// <summary> /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream and leaves space to include a label when fixes are done. /// </summary> /// <param name="opcode">The Microsoft intermediate language (MSIL) instruction to be put onto the stream.</param> /// <param name="label">The label to which to branch from this location.</param> public void Emit(OperationCode opcode, ILGeneratorLabel label) { if (opcode == OperationCode.Br && this.operations.Count > 0) { Operation previousOp = this.operations[this.operations.Count - 1]; if (previousOp.OperationCode == (OperationCode)int.MaxValue) { ILGeneratorLabel labelOfBranch = (ILGeneratorLabel)previousOp.value; if (labelOfBranch.mayAlias) { labelOfBranch.alias = label; } } } this.operations.Add(new Operation(opcode, this.offset, this.GetCurrentSequencePoint(), label)); this.offset += SizeOfOperationCode(opcode) + SizeOfOffset(opcode); }
/// <summary> /// Ends a try body. /// </summary> public void EndTryBody() //^ requires InTryBody; { this.tryBodyStack.Pop(); if (this.handlers.Count > 0) { ILGeneratorLabel handlerEnd = new ILGeneratorLabel(false); this.MarkLabel(handlerEnd); for (int i = this.handlers.Count - 1; i >= 0; i--) { if (this.handlers[i].HandlerEnd == null) { this.handlers[i].HandlerEnd = handlerEnd; break; } } } }
/// <summary> /// Puts the specified instruction onto the stream of instructions. /// </summary> /// <param name="opcode">The Intermediate Language (IL) instruction to be put onto the stream.</param> public void Emit(OperationCode opcode) { if (opcode == OperationCode.Ret) { int i = this.operations.Count; while (--i >= 0) { Operation previousOp = this.operations[i]; if (previousOp.OperationCode != (OperationCode)int.MaxValue) { break; } ILGeneratorLabel labelOfBranch = (ILGeneratorLabel)previousOp.value; labelOfBranch.labelsReturnInstruction = true; } } this.operations.Add(new Operation(opcode, this.offset, this.GetCurrentSequencePoint(), null)); this.offset += SizeOfOperationCode(opcode); }
/// <summary> /// Ends a try body. /// </summary> public void EndTryBody() //^ requires InTryBody; { this.tryBodyStack.Pop(); if (this.handlers.Count > 0) { ILGeneratorLabel handlerEnd = new ILGeneratorLabel(false); this.MarkLabel(handlerEnd); for (int i = this.handlers.Count - 1; i >= 0; i--) { var handler = this.handlers[i]; if (handler.HandlerEnd == null) { handler.HandlerEnd = handlerEnd; if (i < this.handlers.Count - 1) { this.handlers.RemoveAt(i); this.handlers.Add(handler); } break; } } } }
/// <summary> /// Generates IL for the specified conditional. /// </summary> /// <param name="conditional">The conditional.</param> public override void TraverseChildren(IConditional conditional) { ILGeneratorLabel falseCase = new ILGeneratorLabel(); ILGeneratorLabel endif = new ILGeneratorLabel(); this.VisitBranchIfFalse(conditional.Condition, falseCase); this.Traverse(conditional.ResultIfTrue); this.generator.Emit(OperationCode.Br, endif); this.generator.MarkLabel(falseCase); this.StackSize--; this.Traverse(conditional.ResultIfFalse); this.generator.MarkLabel(endif); }
/// <summary> /// Marks the Microsoft intermediate language (MSIL) stream's current position with the given label. /// </summary> public void MarkLabel(ILGeneratorLabel label) { label.Offset = this.offset; this.operations.Add(new Operation((OperationCode)int.MaxValue, this.offset, Dummy.Location, label)); }
public void Compile(string fileName) { string appName = Path.GetFileNameWithoutExtension(fileName); string exeName = appName + ".exe"; string src = ""; using (TextReader file = new StreamReader(fileName)) { src = file.ReadToEnd(); } var nameTable = new NameTable(); using (var host = new PeReader.DefaultHost(nameTable)) { // Load Mirage types IModule module = host.LoadUnitFrom("Mirage.dll") as IModule; if (module == null || module is Dummy) { return; } var machineType = module.GetAllTypes().First(x => x.Name.Value == "Machine"); var inputType = module.GetAllTypes().First(x => x.Name.Value == "ConsoleInput"); var outputType = module.GetAllTypes().First(x => x.Name.Value == "ConsoleOutput"); // Create assembly var coreAssembly = host.LoadAssembly(host.CoreAssemblySymbolicIdentity); var assembly = new Assembly() { Name = nameTable.GetNameFor(appName), ModuleName = nameTable.GetNameFor(exeName), PlatformType = host.PlatformType, Kind = ModuleKind.ConsoleApplication, RequiresStartupStub = host.PointerSize == 4, TargetRuntimeVersion = coreAssembly.TargetRuntimeVersion, }; assembly.AssemblyReferences.Add(coreAssembly); // Create namespace var rootUnitNamespace = new RootUnitNamespace(); assembly.UnitNamespaceRoot = rootUnitNamespace; rootUnitNamespace.Unit = assembly; // Create module class var moduleClass = new NamespaceTypeDefinition() { ContainingUnitNamespace = rootUnitNamespace, InternFactory = host.InternFactory, IsClass = true, Name = nameTable.GetNameFor("<Module>"), }; assembly.AllTypes.Add(moduleClass); // Create program class var programClass = new NamespaceTypeDefinition() { ContainingUnitNamespace = rootUnitNamespace, InternFactory = host.InternFactory, IsClass = true, IsPublic = true, Methods = new List<IMethodDefinition>(1), Name = nameTable.GetNameFor("Program"), }; programClass.BaseClasses = new List<ITypeReference>() { host.PlatformType.SystemObject }; rootUnitNamespace.Members.Add(programClass); // Add types to the assembly assembly.AllTypes.Add(machineType); foreach (var t in machineType.NestedTypes) { assembly.AllTypes.Add(t); } assembly.AllTypes.Add(inputType); assembly.AllTypes.Add(outputType); assembly.AllTypes.Add(programClass); // Create main method var mainMethod = new MethodDefinition() { ContainingTypeDefinition = programClass, InternFactory = host.InternFactory, IsCil = true, IsStatic = true, Name = nameTable.GetNameFor("Main"), Type = host.PlatformType.SystemVoid, Visibility = TypeMemberVisibility.Public, }; assembly.EntryPoint = mainMethod; programClass.Methods.Add(mainMethod); // Create constructors and methods IMethodReference machineConstructor = new Microsoft.Cci.MethodReference( host, machineType, CallingConvention.HasThis, host.PlatformType.SystemVoid, host.NameTable.Ctor, 0 ); IMethodReference inputConstructor = new Microsoft.Cci.MethodReference( host, inputType, CallingConvention.HasThis, host.PlatformType.SystemVoid, host.NameTable.Ctor, 0 ); var inputCast = TypeHelper.GetMethod(inputType, nameTable.GetNameFor("op_Implicit"), inputType); IMethodReference outputConstructor = new Microsoft.Cci.MethodReference( host, outputType, CallingConvention.HasThis, host.PlatformType.SystemVoid, host.NameTable.Ctor, 0 ); var outputCast = TypeHelper.GetMethod(outputType, nameTable.GetNameFor("op_Implicit"), outputType); var opIncPointers = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("IncPointers")); var opDecPointers = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("DecPointers")); var opIncHiPointer = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("IncHiPointer")); var opDecHiPointer = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("DecHiPointer")); var opReflectHiPointer = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("ReflectHiPointer")); var opLoadHiPointer = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("LoadHiPointer")); var opDragLoPointer = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("DragLoPointer")); var opXchPointers = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("XchPointers")); var opClear = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Clear")); var opAdd = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Add")); var opDec = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Dec")); var opNot = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Not")); var opAnd = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("And")); var opOr = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Or")); var opXor = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Xor")); var opSal = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Sal")); var opSar = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Sar")); var opLoadData = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("LoadData"), host.PlatformType.SystemString); var opInput = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Input"), inputCast.Type); var opOutput = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Output"), outputCast.Type); var opJz = TypeHelper.GetMethod(machineType, nameTable.GetNameFor("Jz")); // Create program code var labels = new Stack<ILGeneratorLabel>(100); var ilGenerator = new ILGenerator(host, mainMethod); ilGenerator.Emit(OperationCode.Newobj, machineConstructor); ilGenerator.Emit(OperationCode.Stloc_0); ilGenerator.Emit(OperationCode.Newobj, inputConstructor); ilGenerator.Emit(OperationCode.Stloc_1); ilGenerator.Emit(OperationCode.Newobj, outputConstructor); ilGenerator.Emit(OperationCode.Stloc_2); int pc = 0; while (pc < src.Length) { char opcode = src[pc++]; switch (opcode) { case '>': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opIncPointers); break; case '<': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opDecPointers); break; case ']': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opIncHiPointer); break; case '[': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opDecHiPointer); break; case '#': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opReflectHiPointer); break; case '$': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opLoadHiPointer); break; case '=': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opDragLoPointer); break; case '%': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opXchPointers); break; case '_': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opClear); break; case '+': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opAdd); break; case '-': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opDec); break; case '~': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opNot); break; case '&': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opAnd); break; case '|': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opOr); break; case '^': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opXor); break; case '*': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opSal); break; case '/': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opSar); break; case '(': int dataStart = pc; int dataEnd = dataStart; while (src[pc++] != ')') { dataEnd = pc; } ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Ldstr, src.Substring(dataStart, dataEnd - dataStart)); ilGenerator.Emit(OperationCode.Callvirt, opLoadData); break; case '?': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Ldloc_1); ilGenerator.Emit(OperationCode.Call, inputCast); ilGenerator.Emit(OperationCode.Callvirt, opInput); break; case '!': ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Ldloc_2); ilGenerator.Emit(OperationCode.Call, outputCast); ilGenerator.Emit(OperationCode.Callvirt, opOutput); break; case '{': var cycleStart = new ILGeneratorLabel(); var cycleEnd = new ILGeneratorLabel(); labels.Push(cycleStart); labels.Push(cycleEnd); ilGenerator.Emit(OperationCode.Br, cycleEnd); ilGenerator.MarkLabel(cycleStart); break; case '}': ilGenerator.MarkLabel(labels.Pop()); ilGenerator.Emit(OperationCode.Ldloc_0); ilGenerator.Emit(OperationCode.Callvirt, opJz); ilGenerator.Emit(OperationCode.Ldc_I4_0); ilGenerator.Emit(OperationCode.Ceq); ilGenerator.Emit(OperationCode.Stloc_3); ilGenerator.Emit(OperationCode.Ldloc_3); ilGenerator.Emit(OperationCode.Brtrue, labels.Pop()); break; default: break; } } ilGenerator.Emit(OperationCode.Ret); mainMethod.Body = new ILGeneratorMethodBody( ilGenerator, true, 8, mainMethod, new List<ILocalDefinition>() { new LocalDefinition() { Type = machineType }, new LocalDefinition() { Type = inputType }, new LocalDefinition() { Type = outputType }, new LocalDefinition() { Type = host.PlatformType.SystemInt32 }, }, Enumerable<ITypeDefinition>.Empty ); using (var peStream = File.Create(exeName)) { PeWriter.WritePeToStream(assembly, host, peStream); } } }
/// <summary> /// Begins the part of a filter handler that is invoked on the second pass if the filter condition returns true on the first pass. /// </summary> public void BeginFilterBody() { this.Emit(OperationCode.Endfilter); ILGeneratorLabel handlerStart = new ILGeneratorLabel(false); this.MarkLabel(handlerStart); this.handlers[handlers.Count-1].HandlerStart = handlerStart; }
/// <summary> /// Puts the specified instruction onto the Microsoft intermediate language (MSIL) stream and leaves space to include a label when fixes are done. /// </summary> /// <param name="opcode">The Microsoft intermediate language (MSIL) instruction to be put onto the stream.</param> /// <param name="label">The label to which to branch from this location.</param> public void Emit(OperationCode opcode, ILGeneratorLabel label) { if (opcode == OperationCode.Br && this.operations.Count > 0) { Operation previousOp = this.operations[this.operations.Count-1]; if (previousOp.OperationCode == (OperationCode)int.MaxValue) { ILGeneratorLabel labelOfBranch = (ILGeneratorLabel)previousOp.value; if (labelOfBranch.mayAlias) labelOfBranch.alias = label; } } this.operations.Add(new Operation(opcode, this.offset, this.GetCurrentSequencePoint(), label)); this.offset += SizeOfOperationCode(opcode)+SizeOfOffset(opcode); }
private ILGeneratorLabel GetLabelFor(uint offset) { var result = this.labelFor[offset]; if (result == null) this.labelFor[offset] = result = new ILGeneratorLabel(); return result; }
private void GenerateDownLevelLockStatement(ILockStatement lockStatement) { var systemThreading = new NestedUnitNamespaceReference(this.host.PlatformType.SystemObject.ContainingUnitNamespace, this.host.NameTable.GetNameFor("Threading")); var systemThreadingMonitor = new NamespaceTypeReference(this.host, systemThreading, this.host.NameTable.GetNameFor("Monitor"), 0, isEnum: false, isValueType: false, typeCode: PrimitiveTypeCode.NotPrimitive); var parameters = new IParameterTypeInformation[2]; var monitorEnter = new MethodReference(this.host, systemThreadingMonitor, CallingConvention.Default, this.host.PlatformType.SystemVoid, this.host.NameTable.GetNameFor("Enter"), 0, this.host.PlatformType.SystemObject); var monitorExit = new MethodReference(this.host, systemThreadingMonitor, CallingConvention.Default, this.host.PlatformType.SystemVoid, this.host.NameTable.GetNameFor("Exit"), 0, this.host.PlatformType.SystemObject); this.EmitSequencePoint(lockStatement.Locations); var guardObject = new TemporaryVariable(lockStatement.Guard.Type, this.method); this.Traverse(lockStatement.Guard); this.generator.Emit(OperationCode.Dup); this.StackSize++; this.VisitAssignmentTo(guardObject); this.generator.Emit(OperationCode.Call, monitorEnter); this.StackSize--; //try var savedCurrentTryCatch = this.currentTryCatch; this.currentTryCatch = lockStatement; var savedCurrentTryCatchFinallyEnd = this.currentTryCatchFinallyEnd; this.currentTryCatchFinallyEnd = new ILGeneratorLabel(); this.generator.BeginTryBody(); this.Traverse(lockStatement.Body); if (!this.lastStatementWasUnconditionalTransfer) this.generator.Emit(OperationCode.Leave, this.currentTryCatchFinallyEnd); //finally this.generator.BeginFinallyBlock(); //if (status) this.LoadLocal(guardObject); this.generator.Emit(OperationCode.Call, monitorExit); this.StackSize--; //monitor exit this.generator.Emit(OperationCode.Endfinally); this.generator.EndTryBody(); this.generator.MarkLabel(this.currentTryCatchFinallyEnd); this.currentTryCatchFinallyEnd = savedCurrentTryCatchFinallyEnd; this.currentTryCatch = savedCurrentTryCatch; this.lastStatementWasUnconditionalTransfer = false; }
internal bool LabelIsOutsideCurrentExceptionBlock(ILGeneratorLabel label) { IStatement tryCatchContainingTarget = null; this.mostNestedTryCatchFor.TryGetValue(label, out tryCatchContainingTarget); return this.currentTryCatch != tryCatchContainingTarget; }
// creates a method for a given method CFG (IMPORTANT: it updates the operations // and exception handlers of the existing method, does not create a new method) public void createMethodFromCfg(MethodCfg methodCfg) { // list of basic blocks that belong together logically (i.e. BB2 lies directly behind BB1 because it reaches it without a branch) IList<BasicBlockUnion> basicBlockUnions = new List<BasicBlockUnion>(); // first step: create basic block unions // create first basic block union starting from the start basic block BasicBlockUnion firstBasicBlockUnion = new BasicBlockUnion(); this.createRecursivelyBasicBlockUnion(basicBlockUnions, firstBasicBlockUnion, methodCfg.startBasicBlock); basicBlockUnions.Add(firstBasicBlockUnion); // create basic block unions for the rest of the basic blocks foreach (BasicBlock tempBB in methodCfg.basicBlocks) { // create a basic block union (if basic block is not contained by another existing basic block union) BasicBlockUnion basicBlockUnion = new BasicBlockUnion(); this.createRecursivelyBasicBlockUnion(basicBlockUnions, basicBlockUnion, tempBB); // if the newly created basic block union contains basic blocks // => add to list of basic block unions if (basicBlockUnion.basicBlocks.Count() != 0) { basicBlockUnions.Add(basicBlockUnion); } } // step two: merge basic block unions with respect to the exception handler foreach (OperationExceptionInformation exceptionHandler in methodCfg.method.Body.OperationExceptionInformation) { // create a temporary object to reorder the basic block unions with respect to the exception handler BasicBlockUnionExceptionHandlerOrder tempNewOrder = new BasicBlockUnionExceptionHandlerOrder(); tempNewOrder.exceptionHandler = exceptionHandler; // find all basic blocks that are connected to the current exception handler foreach (BasicBlockUnion basicBlockUnion in basicBlockUnions) { foreach (BasicBlock basicBlock in basicBlockUnion.basicBlocks) { // ignore all basic blocks that do not have any try/catch/finally handler in it if (basicBlock.tryBlocks.Count() == 0 && basicBlock.handlerBlocks.Count() == 0) { continue; } // find the try block that is connected to the current exception handler foreach (TryBlock tryBlock in basicBlock.tryBlocks) { // ignore try blocks that do not belong to the current exception handler if (tryBlock.exceptionHandler != exceptionHandler) { continue; } // check if basic block union already resides in try body // if not => add to try body if (!tempNewOrder.tryBody.Contains(basicBlockUnion)) { tempNewOrder.tryBody.Add(basicBlockUnion); } // check if the basic block starts the try block if (tryBlock.firstBasicBlockOfTryBlock) { tempNewOrder.tryStart = basicBlockUnion; } // check if the basic block ends the try block if (tryBlock.lastBasicBlockOfTryBlock) { tempNewOrder.tryEnd = basicBlockUnion; } } // find the handler block that is connected to the current exception handler foreach (HandlerBlock handlerBlock in basicBlock.handlerBlocks) { // ignore handler blocks that do not belong to the current exception handler if (handlerBlock.exceptionHandler != exceptionHandler) { continue; } // check if basic block union already resides in handler body // if not => add to handler body if (!tempNewOrder.handlerBody.Contains(basicBlockUnion)) { tempNewOrder.handlerBody.Add(basicBlockUnion); } // check if the basic block starts the handler block if (handlerBlock.firstBasicBlockOfHandlerBlock) { tempNewOrder.handlerStart = basicBlockUnion; } // check if the basic block ends the handler block if (handlerBlock.lastBasicBlockOfHandlerBlock) { tempNewOrder.handlerEnd = basicBlockUnion; } } } } if (tempNewOrder.tryStart == null || tempNewOrder.tryEnd == null || tempNewOrder.handlerStart == null || tempNewOrder.handlerEnd == null) { throw new ArgumentException("Could not find all try/catch beginnings/ends."); } // merge basic block unions with respect to the current exception handler // check if the complete exception handling is done in only one basic block union // => ignore it if (tempNewOrder.tryStart == tempNewOrder.tryEnd && tempNewOrder.handlerStart == tempNewOrder.handlerEnd && tempNewOrder.tryStart == tempNewOrder.handlerStart) { continue; } // if not everything lies in the same basic block union // extend the basic block union that starts the try block BasicBlockUnion extendedUnion = tempNewOrder.tryStart; // if try block start and end are the same => nothing to merge // else merge the basic block unions if (tempNewOrder.tryStart != tempNewOrder.tryEnd) { foreach (BasicBlockUnion tryBodyBasicBlockUnion in tempNewOrder.tryBody) { // ignore the basic block union that starts the try block (already in extended union) // and ignore the basic block union that ends the try block (is added last) if (tryBodyBasicBlockUnion == tempNewOrder.tryStart || tryBodyBasicBlockUnion == tempNewOrder.tryEnd) { continue; } // extend first basic block union with the basic block union of the // try block body and remove the added one from the list of basic block unions extendedUnion.basicBlocks.AddRange(tryBodyBasicBlockUnion.basicBlocks); basicBlockUnions.Remove(tryBodyBasicBlockUnion); } // extend first basic block union with the basic block union of the // try block end and remove the added one from the list of basic block unions extendedUnion.basicBlocks.AddRange(tempNewOrder.tryEnd.basicBlocks); basicBlockUnions.Remove(tempNewOrder.tryEnd); } // if the try block end and the handler block start are not the same basic block union // => merge the union of the handler block start to the current basic block union if (tempNewOrder.tryEnd != tempNewOrder.handlerStart) { extendedUnion.basicBlocks.AddRange(tempNewOrder.handlerStart.basicBlocks); basicBlockUnions.Remove(tempNewOrder.handlerStart); } // if handler block start and end are the same => nothing to merge // else merge the basic block unions if (tempNewOrder.handlerStart != tempNewOrder.handlerEnd) { foreach (BasicBlockUnion handlerBodyBasicBlockUnion in tempNewOrder.handlerBody) { // ignore the basic block union that starts the handler block (already in extended union) // and ignore the basic block union that ends the handler block (is added last) if (handlerBodyBasicBlockUnion == tempNewOrder.handlerStart || handlerBodyBasicBlockUnion == tempNewOrder.handlerEnd) { continue; } // add basic block union of the handler block body to the extended basic block union // and remove the added one from the list of basic block unions extendedUnion.basicBlocks.AddRange(handlerBodyBasicBlockUnion.basicBlocks); basicBlockUnions.Remove(handlerBodyBasicBlockUnion); } // extend the extended basic block union with the basic block union of the // handler block end and remove the added one from the list of basic block unions extendedUnion.basicBlocks.AddRange(tempNewOrder.handlerEnd.basicBlocks); basicBlockUnions.Remove(tempNewOrder.handlerEnd); } } // step three: create one list of operations and modify jump offsets // create a list of operations from the basic blocks, // update basic block start/end indices accordingly // and update the offsets of the operations (needed to update branch operations) IList<IOperation> methodOperations = new List<IOperation>(); int operationIdx = 0; uint currentOffset = 0; foreach (BasicBlockUnion basicBlockUnion in basicBlockUnions) { foreach (BasicBlock basicBlock in basicBlockUnion.basicBlocks) { basicBlock.startIdx = operationIdx; //foreach (IOperation operation in basicBlock.operations) { for (int opIdx = 0; opIdx < basicBlock.operations.Count(); opIdx++) { IOperation operation = basicBlock.operations.ElementAt(opIdx); // a new operation object is needed because the old operations // are read only due to cci InternalOperation newOperation = new InternalOperation(); // exchange every short instruction with its larger one switch (operation.OperationCode) { case OperationCode.Beq_S: newOperation.OperationCode = OperationCode.Beq; break; case OperationCode.Bge_S: newOperation.OperationCode = OperationCode.Bge; break; case OperationCode.Bge_Un_S: newOperation.OperationCode = OperationCode.Bge_Un; break; case OperationCode.Bgt_S: newOperation.OperationCode = OperationCode.Bgt; break; case OperationCode.Bgt_Un_S: newOperation.OperationCode = OperationCode.Bgt_Un; break; case OperationCode.Ble_S: newOperation.OperationCode = OperationCode.Ble; break; case OperationCode.Ble_Un_S: newOperation.OperationCode = OperationCode.Ble_Un; break; case OperationCode.Blt_S: newOperation.OperationCode = OperationCode.Blt; break; case OperationCode.Blt_Un_S: newOperation.OperationCode = OperationCode.Blt_Un; break; case OperationCode.Bne_Un_S: newOperation.OperationCode = OperationCode.Bne_Un; break; case OperationCode.Br_S: newOperation.OperationCode = OperationCode.Br; break; case OperationCode.Brfalse_S: newOperation.OperationCode = OperationCode.Brfalse; break; case OperationCode.Brtrue_S: newOperation.OperationCode = OperationCode.Brtrue; break; case OperationCode.Ldarg_S: newOperation.OperationCode = OperationCode.Ldarg; break; case OperationCode.Ldarga_S: newOperation.OperationCode = OperationCode.Ldarga; break; case OperationCode.Ldc_I4_S: newOperation.OperationCode = OperationCode.Ldc_I4; break; case OperationCode.Ldloc_S: newOperation.OperationCode = OperationCode.Ldloc; break; case OperationCode.Ldloca_S: newOperation.OperationCode = OperationCode.Ldloca; break; case OperationCode.Leave_S: newOperation.OperationCode = OperationCode.Leave; break; case OperationCode.Starg_S: newOperation.OperationCode = OperationCode.Starg; break; case OperationCode.Stloc_S: newOperation.OperationCode = OperationCode.Stloc; break; default: newOperation.OperationCode = operation.OperationCode; break; } newOperation.Value = operation.Value; newOperation.Location = operation.Location; newOperation.Offset = currentOffset; methodOperations.Add(newOperation); // replace old operation in basic block with newly created one basicBlock.operations[opIdx] = newOperation; operationIdx++; currentOffset += CfgBuilder.getSizeOfOperation(newOperation); } basicBlock.endIdx = operationIdx - 1; } } // step four: update branches with new target offsets foreach (BasicBlock tempBB in methodCfg.basicBlocks) { InternalOperation branchOperation = (InternalOperation)methodOperations.ElementAt(tempBB.endIdx); // check which kind of exit branch is used by this basic block and update its operation accordingly if (tempBB.exitBranch as NoBranchTarget != null) { continue; } else if (tempBB.exitBranch as TryBlockTarget != null) { continue; } else if (tempBB.exitBranch as ConditionalBranchTarget != null) { ConditionalBranchTarget tempExitBranch = (tempBB.exitBranch as ConditionalBranchTarget); // check if the branch is done by a branch operation if (!CfgBuilder.isBranchOperation(branchOperation)) { throw new ArgumentException("Branch is not done by a valid branch operation."); } // get index of the branch target int targetBranchIdx = tempExitBranch.takenTarget.startIdx; // update offset of the taken branch branchOperation.Value = methodOperations.ElementAt(targetBranchIdx).Offset; } else if (tempBB.exitBranch as SwitchBranchTarget != null) { SwitchBranchTarget tempExitBranch = (tempBB.exitBranch as SwitchBranchTarget); // check if the branch is done by a branch operation if (!CfgBuilder.isBranchOperation(branchOperation)) { throw new ArgumentException("Branch is not done by a valid branch operation."); } // update all switch branches for (int switchIdx = 0; switchIdx < tempExitBranch.takenTarget.Count(); switchIdx++) { // get index of the branch target int targetBranchIdx = tempExitBranch.takenTarget.ElementAt(switchIdx).startIdx; // update offset of the taken branch ((uint[])branchOperation.Value)[switchIdx] = methodOperations.ElementAt(targetBranchIdx).Offset; } } else if (tempBB.exitBranch as UnconditionalBranchTarget != null) { UnconditionalBranchTarget tempExitBranch = (tempBB.exitBranch as UnconditionalBranchTarget); // check if the branch is done by a branch operation if (!CfgBuilder.isBranchOperation(branchOperation)) { throw new ArgumentException("Branch is not done by a valid branch operation."); } // get index of the branch target int targetBranchIdx = tempExitBranch.takenTarget.startIdx; // update offset of the taken branch branchOperation.Value = methodOperations.ElementAt(targetBranchIdx).Offset; } else if (tempBB.exitBranch as ExitBranchTarget != null) { continue; } else if (tempBB.exitBranch as ThrowBranchTarget != null) { continue; } else if (tempBB.exitBranch as ExceptionBranchTarget != null) { ExceptionBranchTarget tempExitBranch = (tempBB.exitBranch as ExceptionBranchTarget); // check if the branch is done by a branch operation if (!CfgBuilder.isBranchOperation(branchOperation)) { throw new ArgumentException("Branch is not done by a valid branch operation."); } // get index of the branch target int targetBranchIdx = tempExitBranch.exitTarget.startIdx; // update offset of the taken branch branchOperation.Value = methodOperations.ElementAt(targetBranchIdx).Offset; } else { throw new ArgumentException("Do not know how to handle exit branch."); } } // step five: create new exception handler with updated offsets and // create new method body of the cci method MethodDefinition method = methodCfg.method; var ilGenerator = new ILGenerator(this.host, method); // emit all operations to the new body foreach (IOperation operation in methodOperations) { ilGenerator.Emit(operation.OperationCode, operation.Value); } // list that is used to replace the pointer to the old exception handler in the basic blocks to the new exception handler List<OldExceptionHandlerMapping> oldExceptionHandlerMappings = new List<OldExceptionHandlerMapping>(); // create for each old exception handler a new one with updated data foreach (IOperationExceptionInformation exceptionHandler in method.Body.OperationExceptionInformation) { // create mapping object for old exception handler OldExceptionHandlerMapping oldExceptionHandlerMapping = new OldExceptionHandlerMapping(); oldExceptionHandlerMapping.oldExceptionHandler = exceptionHandler; oldExceptionHandlerMappings.Add(oldExceptionHandlerMapping); ILGeneratorLabel tryStart = new ILGeneratorLabel(); ILGeneratorLabel tryEnd = new ILGeneratorLabel(); ILGeneratorLabel handlerStart = new ILGeneratorLabel(); ILGeneratorLabel handlerEnd = new ILGeneratorLabel(); ILGeneratorLabel filterStart = new ILGeneratorLabel(); // search for the basic blocks that start/end the try block and handler block foreach (BasicBlock tempBB in methodCfg.basicBlocks) { foreach (TryBlock tempTryBlock in tempBB.tryBlocks) { // ignore try blocks that do not belong to the current exception handler if (tempTryBlock.exceptionHandler != exceptionHandler) { continue; } // get offset of the instruction that starts the try block if (tempTryBlock.firstBasicBlockOfTryBlock) { tryStart.Offset = tempBB.operations.ElementAt(0).Offset; oldExceptionHandlerMapping.tryStartOffset = tryStart.Offset; } // get offset of the instruction that ends the try block (always the last instruction of the basic block) if (tempTryBlock.lastBasicBlockOfTryBlock) { tryEnd.Offset = tempBB.operations.ElementAt(tempBB.operations.Count() - 1).Offset; // exception handler object needs offset of the end of the instruction (not the beginning) tryEnd.Offset += CfgBuilder.getSizeOfOperation(tempBB.operations.ElementAt(tempBB.operations.Count() - 1)); oldExceptionHandlerMapping.tryEndOffset = tryEnd.Offset; } } foreach (HandlerBlock tempHandlerBlock in tempBB.handlerBlocks) { // ignore handler blocks that do not belong to the current exception handler if (tempHandlerBlock.exceptionHandler != exceptionHandler) { continue; } // get offset ot the instruction that starts the handler block if (tempHandlerBlock.firstBasicBlockOfHandlerBlock) { handlerStart.Offset = tempBB.operations.ElementAt(0).Offset; oldExceptionHandlerMapping.handlerStartOffset = handlerStart.Offset; } // get offset of the instruction that ends the handler block if (tempHandlerBlock.lastBasicBlockOfHandlerBlock) { handlerEnd.Offset = tempBB.operations.ElementAt(tempBB.operations.Count() - 1).Offset; // exception handler object needs offset of the end of the instruction (not the beginning) handlerEnd.Offset += CfgBuilder.getSizeOfOperation(tempBB.operations.ElementAt(tempBB.operations.Count() - 1)); oldExceptionHandlerMapping.handlerEndOffset = handlerEnd.Offset; } } } // copy the exception handler filter filterStart.Offset = exceptionHandler.FilterDecisionStartOffset; oldExceptionHandlerMapping.filterStartOffset = filterStart.Offset; // add new exception handler oldExceptionHandlerMapping.exceptionType = exceptionHandler.ExceptionType; oldExceptionHandlerMapping.handlerKind = exceptionHandler.HandlerKind; ilGenerator.AddExceptionHandlerInformation(exceptionHandler.HandlerKind, exceptionHandler.ExceptionType, tryStart, tryEnd, handlerStart, handlerEnd, filterStart); } // create the body List<ILocalDefinition> variableListCopy = new List<ILocalDefinition>(method.Body.LocalVariables); List<ITypeDefinition> privateHelperTypesListCopy = new List<ITypeDefinition>(method.Body.PrivateHelperTypes); var newBody = new ILGeneratorMethodBody(ilGenerator, method.Body.LocalsAreZeroed, 8, method, variableListCopy, privateHelperTypesListCopy); // TODO dynamic max stack size? method.Body = newBody; // step six: replace pointer to the old exception handler with the new exception handler // map all old exception handler to the new objects foreach (IOperationExceptionInformation exceptionHandler in method.Body.OperationExceptionInformation) { // search for the new exception handler object to get the mapping to the old one bool found = false; foreach (OldExceptionHandlerMapping oldExceptionHandlerMapping in oldExceptionHandlerMappings) { if (oldExceptionHandlerMapping.exceptionType == exceptionHandler.ExceptionType && oldExceptionHandlerMapping.handlerKind == exceptionHandler.HandlerKind && oldExceptionHandlerMapping.filterStartOffset == exceptionHandler.FilterDecisionStartOffset && oldExceptionHandlerMapping.tryStartOffset == exceptionHandler.TryStartOffset && oldExceptionHandlerMapping.tryEndOffset == exceptionHandler.TryEndOffset && oldExceptionHandlerMapping.handlerStartOffset == exceptionHandler.HandlerStartOffset && oldExceptionHandlerMapping.handlerEndOffset == exceptionHandler.HandlerEndOffset) { oldExceptionHandlerMapping.newExceptionHandler = exceptionHandler; found = true; break; } } if (!found) { throw new ArgumentException("Not able to map old exception handler to new one."); } } // replace all old exception handler in the basic blocks with the new exception handler foreach (BasicBlock tempBB in methodCfg.basicBlocks) { // replace all exception handler in the try blocks foreach (TryBlock tryBlock in tempBB.tryBlocks) { foreach (OldExceptionHandlerMapping oldExceptionHandlerMapping in oldExceptionHandlerMappings) { if (tryBlock.exceptionHandler == oldExceptionHandlerMapping.oldExceptionHandler) { tryBlock.exceptionHandler = oldExceptionHandlerMapping.newExceptionHandler; break; } } } // replace all exception handler in the handler blocks foreach (HandlerBlock handlerBlock in tempBB.handlerBlocks) { foreach (OldExceptionHandlerMapping oldExceptionHandlerMapping in oldExceptionHandlerMappings) { if (handlerBlock.exceptionHandler == oldExceptionHandlerMapping.oldExceptionHandler) { handlerBlock.exceptionHandler = oldExceptionHandlerMapping.newExceptionHandler; break; } } } } // TODO // DEBUG /* // sanitize method name to store it as a file String invalidChars = System.Text.RegularExpressions.Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars())); String invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars); String fileName = System.Text.RegularExpressions.Regex.Replace(methodCfg.method.ToString(), invalidRegStr, "_"); fileName = fileName.Length >= 230 ? fileName.Substring(0, 230) : fileName; // dump cfg created from the exit branches System.IO.StreamWriter dotFile = new System.IO.StreamWriter("e:\\" + "\\BBUnion_" + fileName + ".dot"); // start .dot file graph dotFile.WriteLine("digraph G {"); for (int bbuidx = 0; bbuidx < basicBlockUnions.Count(); bbuidx++) { BasicBlockUnion basicBlockUnion = basicBlockUnions.ElementAt(bbuidx); // write all basic blocks to .dot file for (int idx = 0; idx < basicBlockUnion.basicBlocks.Count(); idx++) { BasicBlock currentBasicBlock = basicBlockUnion.basicBlocks.ElementAt(idx); // write the current basic block to the file and all its instructions dotFile.WriteLine("BB" + bbuidx.ToString() + "_" + idx.ToString() + " [shape=record]"); bool first = true; for (int opIdx = 0; opIdx < currentBasicBlock.operations.Count(); opIdx++) { var operation = currentBasicBlock.operations.ElementAt(opIdx); if (first) { dotFile.Write("BB" + bbuidx.ToString() + "_" + idx.ToString() + " [label=\"{"); first = false; } else { dotFile.Write("|"); } // insert try block beginnings foreach (var tryBlock in currentBasicBlock.tryBlocks) { if (tryBlock.firstBasicBlockOfTryBlock && opIdx == 0) { dotFile.Write("TRY START (" + tryBlock.exceptionHandler.ExceptionType.ToString() + ")|"); } } // insert catch block beginnings foreach (var handlerBlock in currentBasicBlock.handlerBlocks) { if (handlerBlock.firstBasicBlockOfHandlerBlock && opIdx == 0) { if (handlerBlock.typeOfHandler == HandlerKind.Catch) { dotFile.Write("CATCH START (" + handlerBlock.exceptionHandler.ExceptionType.ToString() + ")|"); } else if (handlerBlock.typeOfHandler == HandlerKind.Finally) { dotFile.Write("FINALLY START (" + handlerBlock.exceptionHandler.ExceptionType.ToString() + ")|"); } else { throw new ArgumentException("Do not know how to handle handler."); } } } // check if instruction has an argument if (operation.Value != null) { dotFile.Write(operation.OperationCode.ToString() + " " + operation.Value.ToString()); } else { dotFile.Write(operation.OperationCode.ToString()); } // insert try block endings foreach (var tryBlock in currentBasicBlock.tryBlocks) { if (tryBlock.lastBasicBlockOfTryBlock && (currentBasicBlock.operations.Count() - 1) == opIdx) { dotFile.Write("|TRY END (" + tryBlock.exceptionHandler.ExceptionType.ToString() + ")"); } } // insert catch block endings foreach (var handlerBlock in currentBasicBlock.handlerBlocks) { if (handlerBlock.lastBasicBlockOfHandlerBlock && (currentBasicBlock.operations.Count() - 1) == opIdx) { if (handlerBlock.typeOfHandler == HandlerKind.Catch) { dotFile.Write("|CATCH END (" + handlerBlock.exceptionHandler.ExceptionType.ToString() + ")"); } else if (handlerBlock.typeOfHandler == HandlerKind.Finally) { dotFile.Write("|FINALLY END (" + handlerBlock.exceptionHandler.ExceptionType.ToString() + ")"); } else { throw new ArgumentException("Do not know how to handle handler."); } } } } dotFile.WriteLine("}\"]"); if ((idx + 1) < basicBlockUnion.basicBlocks.Count()) { dotFile.WriteLine("BB" + bbuidx.ToString() + "_" + idx.ToString() + " -> BB" + bbuidx.ToString() + "_" + (idx + 1).ToString() + "[ color=\"blue\" ]"); } } } // finish graph dotFile.WriteLine("}"); dotFile.Close(); */ }
private void GenerateSwitchInstruction(IEnumerable<ISwitchCase> switchCases, uint maxValue) { ILGeneratorLabel[] labels = new ILGeneratorLabel[maxValue+1]; ILGeneratorLabel defaultLabel = new ILGeneratorLabel(); for (uint i = 0; i <= maxValue; i++) labels[i] = defaultLabel; Dictionary<ISwitchCase, ILGeneratorLabel> labelFor = new Dictionary<ISwitchCase, ILGeneratorLabel>(); bool foundDefault = false; foreach (ISwitchCase switchCase in switchCases) { if (switchCase.IsDefault) { labelFor.Add(switchCase, defaultLabel); foundDefault = true; } else { ILGeneratorLabel caseLabel = new ILGeneratorLabel(); uint i = (uint)((IConvertible)switchCase.Expression.Value).ToUInt64(null); labels[i] = caseLabel; labelFor.Add(switchCase, caseLabel); } } this.generator.Emit(OperationCode.Switch, labels); this.generator.Emit(OperationCode.Br, defaultLabel); ILGeneratorLabel savedCurrentBreakTarget = this.currentBreakTarget; if (!foundDefault) this.currentBreakTarget = defaultLabel; else this.currentBreakTarget = new ILGeneratorLabel(); if (this.currentTryCatch != null) this.mostNestedTryCatchFor.Add(this.currentBreakTarget, this.currentTryCatch); foreach (ISwitchCase switchCase in switchCases) { this.generator.MarkLabel(labelFor[switchCase]); this.Traverse(switchCase); } this.generator.MarkLabel(this.currentBreakTarget); this.currentBreakTarget = savedCurrentBreakTarget; }
/// <summary> /// Generates IL for the specified for statement. /// </summary> /// <param name="forStatement">For statement.</param> public override void TraverseChildren(IForStatement forStatement) { ILGeneratorLabel savedCurrentBreakTarget = this.currentBreakTarget; ILGeneratorLabel savedCurrentContinueTarget = this.currentContinueTarget; this.currentBreakTarget = new ILGeneratorLabel(); this.currentContinueTarget = new ILGeneratorLabel(); if (this.currentTryCatch != null) { this.mostNestedTryCatchFor.Add(this.currentBreakTarget, this.currentTryCatch); this.mostNestedTryCatchFor.Add(this.currentContinueTarget, this.currentTryCatch); } ILGeneratorLabel conditionCheck = new ILGeneratorLabel(); ILGeneratorLabel loopStart = new ILGeneratorLabel(); this.Traverse(forStatement.InitStatements); this.generator.Emit(OperationCode.Br, conditionCheck); this.generator.MarkLabel(loopStart); this.Traverse(forStatement.Body); this.generator.MarkLabel(this.currentContinueTarget); this.Traverse(forStatement.IncrementStatements); this.generator.MarkLabel(conditionCheck); this.EmitSequencePoint(forStatement.Condition.Locations); this.VisitBranchIfTrue(forStatement.Condition, loopStart); this.generator.MarkLabel(this.currentBreakTarget); this.currentBreakTarget = savedCurrentBreakTarget; this.currentContinueTarget = savedCurrentContinueTarget; this.lastStatementWasUnconditionalTransfer = false; }
/// <summary> /// Generates IL for the specified do until statement. /// </summary> /// <param name="doUntilStatement">The do until statement.</param> public override void TraverseChildren(IDoUntilStatement doUntilStatement) { ILGeneratorLabel savedCurrentBreakTarget = this.currentBreakTarget; ILGeneratorLabel savedCurrentContinueTarget = this.currentContinueTarget; this.currentBreakTarget = new ILGeneratorLabel(); this.currentContinueTarget = new ILGeneratorLabel(); if (this.currentTryCatch != null) { this.mostNestedTryCatchFor.Add(this.currentBreakTarget, this.currentTryCatch); this.mostNestedTryCatchFor.Add(this.currentContinueTarget, this.currentTryCatch); } this.generator.MarkLabel(this.currentContinueTarget); this.Traverse(doUntilStatement.Body); this.EmitSequencePoint(doUntilStatement.Condition.Locations); this.VisitBranchIfFalse(doUntilStatement.Condition, this.currentContinueTarget); this.generator.MarkLabel(this.currentBreakTarget); this.currentBreakTarget = savedCurrentBreakTarget; this.currentContinueTarget = savedCurrentContinueTarget; this.lastStatementWasUnconditionalTransfer = false; }
/// <summary> /// Generates IL for the specified conditional statement. /// </summary> /// <param name="conditionalStatement">The conditional statement.</param> public override void TraverseChildren(IConditionalStatement conditionalStatement) { this.EmitSequencePoint(conditionalStatement.Condition.Locations); ILGeneratorLabel/*?*/ endif = null; if (conditionalStatement.TrueBranch is IBreakStatement && !this.LabelIsOutsideCurrentExceptionBlock(this.currentBreakTarget)) this.VisitBranchIfTrue(conditionalStatement.Condition, this.currentBreakTarget); else if (conditionalStatement.TrueBranch is IContinueStatement && !this.LabelIsOutsideCurrentExceptionBlock(this.currentContinueTarget)) this.VisitBranchIfTrue(conditionalStatement.Condition, this.currentContinueTarget); else { ILGeneratorLabel falseCase = new ILGeneratorLabel(); this.VisitBranchIfFalse(conditionalStatement.Condition, falseCase); this.Traverse(conditionalStatement.TrueBranch); if (!this.lastStatementWasUnconditionalTransfer) { endif = new ILGeneratorLabel(); this.generator.Emit(OperationCode.Br, endif); } else { } this.generator.MarkLabel(falseCase); } this.Traverse(conditionalStatement.FalseBranch); if (endif != null) this.generator.MarkLabel(endif); this.lastStatementWasUnconditionalTransfer = false; }
/// <summary> /// Generates IL for the specified logical not. /// </summary> /// <param name="logicalNot">The logical not.</param> public override void TraverseChildren(ILogicalNot logicalNot) { if (logicalNot.Operand.Type.IsValueType) { //The type should be a primitive integer, a boolean or an enum. this.Traverse(logicalNot.Operand); var opsize = TypeHelper.SizeOfType(logicalNot.Operand.Type); if (opsize == 1 || opsize == 2 || opsize == 4) { this.generator.Emit(OperationCode.Ldc_I4_0); this.StackSize++; this.generator.Emit(OperationCode.Ceq); this.StackSize--; } else if (opsize == 8) { this.generator.Emit(OperationCode.Ldc_I4_0); this.StackSize++; this.generator.Emit(OperationCode.Conv_I8); this.generator.Emit(OperationCode.Ceq); this.StackSize--; } else { Contract.Assert(opsize == 0); //If not, the CodeModel is invalid. //the type is an unresolved reference, typically an enum, so we just don't know what size it is (at compile time, that is). var trueCase = new ILGeneratorLabel(); var done = new ILGeneratorLabel(); this.generator.Emit(OperationCode.Brtrue_S, trueCase); this.generator.Emit(OperationCode.Ldc_I4_0); this.generator.Emit(OperationCode.Br_S, done); this.generator.MarkLabel(trueCase); this.generator.Emit(OperationCode.Ldc_I4_1); this.generator.MarkLabel(done); } } else { //pointer non null test this.Traverse(logicalNot.Operand); this.generator.Emit(OperationCode.Ldnull); this.StackSize++; this.generator.Emit(OperationCode.Ceq); this.StackSize--; } }
/// <summary> /// Generates IL code for the given for each statement for the special case where the collection is known /// to be vector type. /// </summary> /// <param name="forEachStatement">The foreach statement to visit.</param> /// <param name="arrayType">The vector type of the collection.</param> public virtual void VisitForeachArrayElement(IForEachStatement forEachStatement, IArrayTypeReference arrayType) { Contract.Requires(arrayType.IsVector); ILGeneratorLabel savedCurrentBreakTarget = this.currentBreakTarget; ILGeneratorLabel savedCurrentContinueTarget = this.currentContinueTarget; this.currentBreakTarget = new ILGeneratorLabel(); this.currentContinueTarget = new ILGeneratorLabel(); if (this.currentTryCatch != null) { this.mostNestedTryCatchFor.Add(this.currentBreakTarget, this.currentTryCatch); this.mostNestedTryCatchFor.Add(this.currentContinueTarget, this.currentTryCatch); } ILGeneratorLabel conditionCheck = new ILGeneratorLabel(); ILGeneratorLabel loopStart = new ILGeneratorLabel(); this.EmitSequencePoint(forEachStatement.Variable.Locations); this.Traverse(forEachStatement.Collection); this.generator.Emit(OperationCode.Dup); var array = new TemporaryVariable(arrayType, this.method); this.VisitAssignmentTo(array); var length = new TemporaryVariable(this.host.PlatformType.SystemInt32, this.method); this.generator.Emit(OperationCode.Ldlen); this.generator.Emit(OperationCode.Conv_I4); this.VisitAssignmentTo(length); var counter = new TemporaryVariable(this.host.PlatformType.SystemInt32, this.method); this.generator.Emit(OperationCode.Ldc_I4_0); this.StackSize++; this.VisitAssignmentTo(counter); this.generator.Emit(OperationCode.Br, conditionCheck); this.generator.MarkLabel(loopStart); this.LoadLocal(array); this.LoadLocal(counter); this.LoadVectorElement(arrayType.ElementType); this.VisitAssignmentTo(forEachStatement.Variable); this.Traverse(forEachStatement.Body); this.generator.MarkLabel(this.currentContinueTarget); this.LoadLocal(counter); this.generator.Emit(OperationCode.Ldc_I4_1); this.StackSize++; this.generator.Emit(OperationCode.Add); this.StackSize--; this.VisitAssignmentTo(counter); this.generator.MarkLabel(conditionCheck); this.EmitSequencePoint(forEachStatement.Collection.Locations); this.LoadLocal(counter); this.LoadLocal(length); this.generator.Emit(OperationCode.Blt, loopStart); this.generator.MarkLabel(this.currentBreakTarget); this.currentBreakTarget = savedCurrentBreakTarget; this.currentContinueTarget = savedCurrentContinueTarget; }
private void InstrumentBlock(BasicBlock<Instruction> block) { var blockOffset = block.Instructions[0].Operation.Offset; this.ilGenerator.MarkLabel(this.GetLabelFor(blockOffset)); for (int i = 0, n = block.Instructions.Count-1; i < n; i++) { var operation = block.Instructions[i].Operation; this.EmitDebugInformationFor(operation); this.ilGenerator.Emit(operation.OperationCode, operation.Value); } var lastOperation = block.Instructions[block.Instructions.Count-1].Operation; this.EmitDebugInformationFor(lastOperation); switch (lastOperation.OperationCode) { case OperationCode.Beq: case OperationCode.Beq_S: case OperationCode.Bge: case OperationCode.Bge_S: case OperationCode.Bge_Un: case OperationCode.Bge_Un_S: case OperationCode.Bgt: case OperationCode.Bgt_S: case OperationCode.Bgt_Un: case OperationCode.Bgt_Un_S: case OperationCode.Ble: case OperationCode.Ble_S: case OperationCode.Ble_Un: case OperationCode.Ble_Un_S: case OperationCode.Blt: case OperationCode.Blt_S: case OperationCode.Blt_Un: case OperationCode.Blt_Un_S: case OperationCode.Bne_Un: case OperationCode.Bne_Un_S: case OperationCode.Brfalse: case OperationCode.Brfalse_S: case OperationCode.Brtrue: case OperationCode.Brtrue_S: var unconditionalBranch = new ILGeneratorLabel(); var fallThrough = new ILGeneratorLabel(); this.ilGenerator.Emit(lastOperation.OperationCode, unconditionalBranch); this.ilGenerator.Emit(OperationCode.Br_S, fallThrough); this.ilGenerator.MarkLabel(unconditionalBranch); this.EmitCounterIncrement(lastOperation.Offset); this.ilGenerator.Emit(OperationCode.Br, this.GetLabelFor((uint)lastOperation.Value)); this.ilGenerator.MarkLabel(fallThrough); this.EmitCounterIncrement(lastOperation.Offset+1); break; case OperationCode.Br: case OperationCode.Br_S: this.EmitCounterIncrement(lastOperation.Offset); this.ilGenerator.Emit(OperationCode.Br, this.GetLabelFor((uint)lastOperation.Value)); break; case OperationCode.Leave: case OperationCode.Leave_S: this.EmitCounterIncrement(lastOperation.Offset); this.ilGenerator.Emit(OperationCode.Leave, this.GetLabelFor((uint)lastOperation.Value)); break; case OperationCode.Endfilter: case OperationCode.Endfinally: case OperationCode.Jmp: case OperationCode.Ret: case OperationCode.Rethrow: case OperationCode.Throw: //No need to count the outgoing edge. Its count is the same as the number of times this block executes. this.ilGenerator.Emit(lastOperation.OperationCode, lastOperation.Value); break; case OperationCode.Switch: fallThrough = new ILGeneratorLabel(); uint[] targets = (uint[])lastOperation.Value; ILGeneratorLabel[] counters = new ILGeneratorLabel[targets.Length]; for (int i = 0, n = counters.Length; i < n; i++) counters[i] = new ILGeneratorLabel(); this.ilGenerator.Emit(OperationCode.Switch, counters); this.EmitCounterIncrement(lastOperation.Offset); this.ilGenerator.Emit(OperationCode.Br, fallThrough); for (int i = 0, n = counters.Length; i < n; i++) { var counterLabel = counters[i]; this.ilGenerator.MarkLabel(counterLabel); this.EmitCounterIncrement(lastOperation.Offset+1+(uint)i); this.ilGenerator.Emit(OperationCode.Br, this.GetLabelFor(targets[i])); } this.ilGenerator.MarkLabel(fallThrough); break; default: this.ilGenerator.Emit(lastOperation.OperationCode, lastOperation.Value); this.EmitCounterIncrement(lastOperation.Offset); break; } }
/// <summary> /// Returns a label that represents the given offset in the original IL. This label must be marked /// at the corresponding location in the rewritten IL. /// </summary> protected virtual ILGeneratorLabel GetLabelFor(uint offset) { Contract.Ensures(Contract.Result<ILGeneratorLabel>() != null); var result = this.labelFor[offset]; if (result == null) this.labelFor[offset] = result = new ILGeneratorLabel(); return result; }
private void ProcessOperations(IMethodBody methodBody) { List<IOperation> operations = ((methodBody.Operations == null) ? new List<IOperation>(): new List<IOperation>(methodBody.Operations)); int count = operations.Count; ILGenerator generator = new ILGenerator(this.host, methodBody.MethodDefinition); if (this.pdbReader != null) { foreach (var ns in this.pdbReader.GetNamespaceScopes(methodBody)) { foreach (var uns in ns.UsedNamespaces) generator.UseNamespace(uns.NamespaceName.Value); } } this.currentGenerator = generator; this.scopeEnumerator = this.pdbReader == null ? null : this.pdbReader.GetLocalScopes(methodBody).GetEnumerator(); this.scopeEnumeratorIsValid = this.scopeEnumerator != null && this.scopeEnumerator.MoveNext(); var methodName = MemberHelper.GetMemberSignature(methodBody.MethodDefinition, NameFormattingOptions.SmartTypeName); #region Record all offsets that appear as part of an exception handler Dictionary<uint, bool> offsetsUsedInExceptionInformation = new Dictionary<uint, bool>(); foreach (var exceptionInfo in methodBody.OperationExceptionInformation??Enumerable<IOperationExceptionInformation>.Empty) { uint x = exceptionInfo.TryStartOffset; if (!offsetsUsedInExceptionInformation.ContainsKey(x)) offsetsUsedInExceptionInformation.Add(x, true); x = exceptionInfo.TryEndOffset; if (!offsetsUsedInExceptionInformation.ContainsKey(x)) offsetsUsedInExceptionInformation.Add(x, true); x = exceptionInfo.HandlerStartOffset; if (!offsetsUsedInExceptionInformation.ContainsKey(x)) offsetsUsedInExceptionInformation.Add(x, true); x = exceptionInfo.HandlerEndOffset; if (!offsetsUsedInExceptionInformation.ContainsKey(x)) offsetsUsedInExceptionInformation.Add(x, true); if (exceptionInfo.HandlerKind == HandlerKind.Filter) { x = exceptionInfo.FilterDecisionStartOffset; if (!offsetsUsedInExceptionInformation.ContainsKey(x)) offsetsUsedInExceptionInformation.Add(x, true); } } #endregion Record all offsets that appear as part of an exception handler Dictionary<uint, ILGeneratorLabel> offset2Label = new Dictionary<uint, ILGeneratorLabel>(); #region Pass 1: Make a label for each branch target for (int i = 0; i < count; i++) { IOperation op = operations[i]; switch (op.OperationCode) { case OperationCode.Beq: case OperationCode.Bge: case OperationCode.Bge_Un: case OperationCode.Bgt: case OperationCode.Bgt_Un: case OperationCode.Ble: case OperationCode.Ble_Un: case OperationCode.Blt: case OperationCode.Blt_Un: case OperationCode.Bne_Un: case OperationCode.Br: case OperationCode.Brfalse: case OperationCode.Brtrue: case OperationCode.Leave: case OperationCode.Beq_S: case OperationCode.Bge_S: case OperationCode.Bge_Un_S: case OperationCode.Bgt_S: case OperationCode.Bgt_Un_S: case OperationCode.Ble_S: case OperationCode.Ble_Un_S: case OperationCode.Blt_S: case OperationCode.Blt_Un_S: case OperationCode.Bne_Un_S: case OperationCode.Br_S: case OperationCode.Brfalse_S: case OperationCode.Brtrue_S: case OperationCode.Leave_S: uint x = (uint)op.Value; if (!offset2Label.ContainsKey(x)) offset2Label.Add(x, new ILGeneratorLabel()); break; case OperationCode.Switch: uint[] offsets = op.Value as uint[]; foreach (var offset in offsets) { if (!offset2Label.ContainsKey(offset)) offset2Label.Add(offset, new ILGeneratorLabel()); } break; default: break; } } #endregion Pass 1: Make a label for each branch target #region Pass 2: Emit each operation, along with labels for (int i = 0; i < count; i++) { IOperation op = operations[i]; ILGeneratorLabel label; this.EmitDebugInformationFor(op); #region Mark operation if it is a label for a branch if (offset2Label.TryGetValue(op.Offset, out label)) { generator.MarkLabel(label); } #endregion Mark operation if it is a label for a branch #region Mark operation if it is pointed to by an exception handler bool ignore; uint offset = op.Offset; if (offsetsUsedInExceptionInformation.TryGetValue(offset, out ignore)) { foreach (var exceptionInfo in methodBody.OperationExceptionInformation) { if (offset == exceptionInfo.TryStartOffset) generator.BeginTryBody(); // Never need to do anthing when offset == exceptionInfo.TryEndOffset because // we pick up an EndTryBody from the HandlerEndOffset below // generator.EndTryBody(); if (offset == exceptionInfo.HandlerStartOffset) { switch (exceptionInfo.HandlerKind) { case HandlerKind.Catch: generator.BeginCatchBlock(exceptionInfo.ExceptionType); break; case HandlerKind.Fault: generator.BeginFaultBlock(); break; case HandlerKind.Filter: generator.BeginFilterBody(); break; case HandlerKind.Finally: generator.BeginFinallyBlock(); break; } } if (exceptionInfo.HandlerKind == HandlerKind.Filter && offset == exceptionInfo.FilterDecisionStartOffset) { generator.BeginFilterBlock(); } if (offset == exceptionInfo.HandlerEndOffset) generator.EndTryBody(); } } #endregion Mark operation if it is pointed to by an exception handler #region Emit operation along with any injection switch (op.OperationCode) { #region Branches case OperationCode.Beq: case OperationCode.Bge: case OperationCode.Bge_Un: case OperationCode.Bgt: case OperationCode.Bgt_Un: case OperationCode.Ble: case OperationCode.Ble_Un: case OperationCode.Blt: case OperationCode.Blt_Un: case OperationCode.Bne_Un: case OperationCode.Br: case OperationCode.Brfalse: case OperationCode.Brtrue: case OperationCode.Leave: case OperationCode.Beq_S: case OperationCode.Bge_S: case OperationCode.Bge_Un_S: case OperationCode.Bgt_S: case OperationCode.Bgt_Un_S: case OperationCode.Ble_S: case OperationCode.Ble_Un_S: case OperationCode.Blt_S: case OperationCode.Blt_Un_S: case OperationCode.Bne_Un_S: case OperationCode.Br_S: case OperationCode.Brfalse_S: case OperationCode.Brtrue_S: case OperationCode.Leave_S: generator.Emit(ILGenerator.LongVersionOf(op.OperationCode), offset2Label[(uint)op.Value]); break; case OperationCode.Switch: uint[] offsets = op.Value as uint[]; ILGeneratorLabel[] labels = new ILGeneratorLabel[offsets.Length]; for (int j = 0, n = offsets.Length; j < n; j++) { labels[j] = offset2Label[offsets[j]]; } generator.Emit(OperationCode.Switch, labels); break; #endregion Branches #region Everything else case OperationCode.Stloc_0: case OperationCode.Stloc_1: case OperationCode.Stloc_2: case OperationCode.Stloc_3: generator.Emit(op.OperationCode); EmitStoreLocal(generator, op); break; case OperationCode.Stloc: case OperationCode.Stloc_S: generator.Emit(op.OperationCode, op.Value); EmitStoreLocal(generator, op); break; default: if (op.Value == null) { generator.Emit(op.OperationCode); break; } var typeCode = System.Convert.GetTypeCode(op.Value); switch (typeCode) { case TypeCode.Byte: generator.Emit(op.OperationCode, (byte)op.Value); break; case TypeCode.Double: generator.Emit(op.OperationCode, (double)op.Value); break; case TypeCode.Int16: generator.Emit(op.OperationCode, (short)op.Value); break; case TypeCode.Int32: generator.Emit(op.OperationCode, (int)op.Value); break; case TypeCode.Int64: generator.Emit(op.OperationCode, (long)op.Value); break; case TypeCode.Object: IFieldReference fieldReference = op.Value as IFieldReference; if (fieldReference != null) { generator.Emit(op.OperationCode, this.Rewrite(fieldReference)); break; } ILocalDefinition localDefinition = op.Value as ILocalDefinition; if (localDefinition != null) { generator.Emit(op.OperationCode, localDefinition); break; } IMethodReference methodReference = op.Value as IMethodReference; if (methodReference != null) { generator.Emit(op.OperationCode, this.Rewrite(methodReference)); break; } IParameterDefinition parameterDefinition = op.Value as IParameterDefinition; if (parameterDefinition != null) { generator.Emit(op.OperationCode, parameterDefinition); break; } ISignature signature = op.Value as ISignature; if (signature != null) { generator.Emit(op.OperationCode, signature); break; } ITypeReference typeReference = op.Value as ITypeReference; if (typeReference != null) { generator.Emit(op.OperationCode, this.Rewrite(typeReference)); break; } throw new ILMutatorException("Should never get here: no other IOperation argument types should exist"); case TypeCode.SByte: generator.Emit(op.OperationCode, (sbyte)op.Value); break; case TypeCode.Single: generator.Emit(op.OperationCode, (float)op.Value); break; case TypeCode.String: generator.Emit(op.OperationCode, (string)op.Value); break; default: // The other cases are the other enum values that TypeCode has. // But no other argument types should be in the Operations. ILGenerator cannot handle anything else, // so such IOperations should never exist. //case TypeCode.Boolean: //case TypeCode.Char: //case TypeCode.DateTime: //case TypeCode.DBNull: //case TypeCode.Decimal: //case TypeCode.Empty: // this would be the value for null, but the case when op.Value is null is handled before the switch statement //case TypeCode.UInt16: //case TypeCode.UInt32: //case TypeCode.UInt64: throw new ILMutatorException("Should never get here: no other IOperation argument types should exist"); } break; #endregion Everything else } #endregion Emit operation along with any injection } while (generator.InTryBody) generator.EndTryBody(); while (this.scopeStack.Count > 0) { this.currentGenerator.EndScope(); this.scopeStack.Pop(); } #endregion Pass 2: Emit each operation, along with labels }
/// <summary> /// This method is called for all labels. /// </summary> /// <param name="op">The Microsoft intermediate language (MSIL) instruction that is marked as being a label.</param> /// <param name="label">A label that can be used to designate locations in MSIL where an instruction may jump.</param> protected virtual void RewriteLabel(IOperation op, ILGeneratorLabel label) { Contract.Requires(this.generator != null); Contract.Requires(op != null); Contract.Requires(label != null); this.generator.MarkLabel(label); }
internal TryBody(ILGeneratorLabel start) { this.start = start; }
/// <summary> /// Generates IL for the specified try catch filter finally statement. /// </summary> /// <param name="tryCatchFilterFinallyStatement">The try catch filter finally statement.</param> public override void TraverseChildren(ITryCatchFinallyStatement tryCatchFilterFinallyStatement) { var savedCurrentTryCatch = this.currentTryCatch; this.currentTryCatch = tryCatchFilterFinallyStatement; ILGeneratorLabel/*?*/ savedCurrentTryCatchFinallyEnd = this.currentTryCatchFinallyEnd; this.currentTryCatchFinallyEnd = new ILGeneratorLabel(); this.generator.BeginTryBody(); this.Traverse(tryCatchFilterFinallyStatement.TryBody); if (!this.lastStatementWasUnconditionalTransfer) this.generator.Emit(OperationCode.Leave, this.currentTryCatchFinallyEnd); this.Traverse(tryCatchFilterFinallyStatement.CatchClauses); if (tryCatchFilterFinallyStatement.FinallyBody != null) { this.generator.BeginFinallyBlock(); this.Traverse(tryCatchFilterFinallyStatement.FinallyBody); this.generator.Emit(OperationCode.Endfinally); } if (tryCatchFilterFinallyStatement.FaultBody != null) { this.generator.BeginFaultBlock(); this.Traverse(tryCatchFilterFinallyStatement.FaultBody); this.generator.Emit(OperationCode.Endfinally); } this.generator.EndTryBody(); this.generator.MarkLabel(this.currentTryCatchFinallyEnd); this.currentTryCatchFinallyEnd = savedCurrentTryCatchFinallyEnd; this.currentTryCatch = savedCurrentTryCatch; this.lastStatementWasUnconditionalTransfer = false; }
//^ ensures InTryBody; /// <summary> /// Begins the body of a try statement. /// </summary> public void BeginTryBody() { ILGeneratorLabel tryBodyStart = new ILGeneratorLabel(false); this.MarkLabel(tryBodyStart); this.tryBodyStack.Push(new TryBody(tryBodyStart)); }
/// <summary> /// Generates IL for the specified resource use statement. /// </summary> /// <param name="resourceUseStatement">The resource use statement.</param> public override void TraverseChildren(IResourceUseStatement resourceUseStatement) { this.EmitSequencePoint(resourceUseStatement.Locations); var systemIDisposable = new NamespaceTypeReference(this.host, this.host.PlatformType.SystemObject.ContainingUnitNamespace, this.host.NameTable.GetNameFor("IDisposable"), 0, isEnum: false, isValueType: false, typeCode: PrimitiveTypeCode.NotPrimitive); var dispose = new MethodReference(this.host, systemIDisposable, CallingConvention.Default, this.host.PlatformType.SystemVoid, this.host.NameTable.GetNameFor("Dispose"), 0, Enumerable<IParameterTypeInformation>.Empty); //Get resource into a local ILocalDefinition resourceLocal; var localDeclaration = resourceUseStatement.ResourceAcquisitions as ILocalDeclarationStatement; if (localDeclaration != null) { resourceLocal = localDeclaration.LocalVariable; this.Traverse(localDeclaration.InitialValue); } else { var expressionStatement = (IExpressionStatement)resourceUseStatement.ResourceAcquisitions; this.Traverse(expressionStatement.Expression); resourceLocal = new TemporaryVariable(systemIDisposable, this.method); } this.VisitAssignmentTo(resourceLocal); //try var savedCurrentTryCatch = this.currentTryCatch; this.currentTryCatch = resourceUseStatement; var savedCurrentTryCatchFinallyEnd = this.currentTryCatchFinallyEnd; this.currentTryCatchFinallyEnd = new ILGeneratorLabel(); this.generator.BeginTryBody(); this.Traverse(resourceUseStatement.Body); if (!this.lastStatementWasUnconditionalTransfer) this.generator.Emit(OperationCode.Leave, this.currentTryCatchFinallyEnd); //finally this.generator.BeginFinallyBlock(); var endOfFinally = new ILGeneratorLabel(); if (!resourceLocal.Type.IsValueType) { this.generator.Emit(OperationCode.Ldloc, resourceLocal); this.generator.Emit(OperationCode.Brfalse_S, endOfFinally); } this.generator.Emit(OperationCode.Ldloc, resourceLocal); this.generator.Emit(OperationCode.Callvirt, dispose); this.generator.MarkLabel(endOfFinally); this.generator.Emit(OperationCode.Endfinally); this.generator.EndTryBody(); this.generator.MarkLabel(this.currentTryCatchFinallyEnd); this.currentTryCatchFinallyEnd = savedCurrentTryCatchFinallyEnd; this.currentTryCatch = savedCurrentTryCatch; this.lastStatementWasUnconditionalTransfer = false; }
//^ requires InTryBody; /// <summary> /// Ends a try body. /// </summary> public void EndTryBody() { this.tryBodyStack.Pop(); if (this.handlers.Count > 0) { ILGeneratorLabel handlerEnd = new ILGeneratorLabel(false); this.MarkLabel(handlerEnd); for (int i = this.handlers.Count-1; i >= 0; i--) { var handler = this.handlers[i]; if (handler.HandlerEnd == null) { handler.HandlerEnd = handlerEnd; if (i < this.handlers.Count-1) { this.handlers.RemoveAt(i); this.handlers.Add(handler); } break; } } } }
/// <summary> /// Performs one or more extra passes over the list of operations, changing long branches to short if possible and short branches to /// long branches if necessary. /// </summary> /// <remarks>If any long branches in this.operations could have been short, they are adjusted to be short. /// This can result in an updated version of this.operations where some branches that had to be long in the previous /// version can now be short as well. Consequently, the adjustment process iterates until no further changes are possible. /// Note that all decisions are made based on the offsets at the start of an iteration. </remarks> public void AdjustBranchSizesToBestFit() { int adjustment; uint numberOfAdjustments; do { adjustment = 0; numberOfAdjustments = 0; for (int i = 0, n = this.operations.Count; i < n; i++) { Operation operation = this.operations[i]; uint oldOffset = operation.offset; uint newOffset = (uint)(((int)oldOffset) + adjustment); operation.offset = newOffset; ILGeneratorLabel /*?*/ label = operation.value as ILGeneratorLabel; if (label != null) { if (operation.OperationCode == (OperationCode)int.MaxValue) { //Dummy operation that serves as label definition. label.Offset = operation.offset; continue; } // REVIEW: Do we really want to do this? Should it be an optimization that is made upstream? if (label.labelsReturnInstruction && (operation.OperationCode == OperationCode.Br || operation.OperationCode == OperationCode.Br_S)) { numberOfAdjustments++; adjustment -= (operation.OperationCode == OperationCode.Br ? 4 : 1); this.operations[i] = new Operation(OperationCode.Ret, operation.offset, label.locationOfReturnInstruction, null); continue; } //For backward branches, this test will compare the new offset of the label with the old offset of the current //instruction. This is OK, because the new offset of the label will be less than or equal to its old offset. bool isForwardBranch = label.Offset >= oldOffset; // Short offsets are calculated from the start of the instruction *after* the current instruction, which takes up 2 bytes // (1 for the opcode and 1 for the signed byte). bool shortOffsetOk = isForwardBranch ? label.Offset - oldOffset <= 129 : newOffset - label.Offset <= 126; OperationCode oldOpCode = operation.OperationCode; if (shortOffsetOk) { operation.operationCode = ShortVersionOf(operation.OperationCode); if (operation.operationCode != oldOpCode) { numberOfAdjustments++; adjustment -= 3; } } else { if (operation.operationCode != LongVersionOf(operation.operationCode)) { throw new InvalidOperationException(); //A short branch was specified for an offset that is long. } //The test for isForwardBranch depends on label offsets only decreasing, so it is not an option to replace the short branch with a long one. } if (operation.OperationCode == OperationCode.Br_S && operation.offset + 2 == label.Offset) { //eliminate branch to the next instruction operation.operationCode = (OperationCode)int.MaxValue; numberOfAdjustments++; adjustment -= 2; } } } } while (numberOfAdjustments > 0); }
//^ requires InTryBody; private ExceptionHandler BeginHandler(HandlerKind kind) { ILGeneratorLabel handlerStart = new ILGeneratorLabel(false); this.MarkLabel(handlerStart); TryBody currentTryBody = this.tryBodyStack.Peek(); ExceptionHandler handler = new ExceptionHandler(kind, currentTryBody, handlerStart); if (currentTryBody.end == null) currentTryBody.end = handlerStart; else if (this.handlers.Count > 0) { for (int i = this.handlers.Count-1; i >= 0; i--) { if (this.handlers[i].HandlerEnd == null) { this.handlers[i].HandlerEnd = handlerStart; break; } } } this.handlers.Add(handler); return handler; }
/// <summary> /// Emits the given operation at the current position of the new IL stream. Also tracks any referenced local definitions, /// so that this.localVariables will contain the exact list of locals used in the new method body. /// </summary> /// <param name="operation"></param> protected virtual void EmitOperation(IOperation operation) { Contract.Requires(operation != null); var operationCode = operation.OperationCode; var value = operation.Value; switch (operationCode) { case OperationCode.Beq: case OperationCode.Bge: case OperationCode.Bge_Un: case OperationCode.Bgt: case OperationCode.Bgt_Un: case OperationCode.Ble: case OperationCode.Ble_Un: case OperationCode.Blt: case OperationCode.Blt_Un: case OperationCode.Bne_Un: case OperationCode.Br: case OperationCode.Br_S: case OperationCode.Brfalse: case OperationCode.Brtrue: case OperationCode.Leave: case OperationCode.Beq_S: case OperationCode.Bge_S: case OperationCode.Bge_Un_S: case OperationCode.Bgt_S: case OperationCode.Bgt_Un_S: case OperationCode.Ble_S: case OperationCode.Ble_Un_S: case OperationCode.Blt_S: case OperationCode.Blt_Un_S: case OperationCode.Bne_Un_S: case OperationCode.Brfalse_S: case OperationCode.Brtrue_S: case OperationCode.Leave_S: operationCode = ILGenerator.LongVersionOf(operationCode); Contract.Assume(operation.Value is uint); value = this.GetLabelFor(+(uint)operation.Value); break; case OperationCode.Switch: uint[] offsets = operation.Value as uint[]; Contract.Assume(offsets != null); var n = offsets.Length; ILGeneratorLabel[] labels = new ILGeneratorLabel[n]; for (int i = 0; i < n; i++) { var offset = offsets[i]; labels[i] = this.GetLabelFor(offset); } value = labels; break; //Avoid the short forms because the locals can get reordered. case OperationCode.Ldloc_0: case OperationCode.Ldloc_1: case OperationCode.Ldloc_2: case OperationCode.Ldloc_3: case OperationCode.Ldloc_S: operationCode = OperationCode.Ldloc; break; case OperationCode.Ldloca_S: operationCode = OperationCode.Ldloca; break; case OperationCode.Stloc_0: case OperationCode.Stloc_1: case OperationCode.Stloc_2: case OperationCode.Stloc_3: case OperationCode.Stloc_S: operationCode = OperationCode.Stloc; break; } this.generator.Emit(operationCode, value); }
private void VisitBranchIfTrue(IExpression expression, ILGeneratorLabel targetLabel) { OperationCode branchOp = OperationCode.Brtrue; IBinaryOperation/*?*/ binaryOperation = expression as IBinaryOperation; bool signedPrimitive = binaryOperation != null && TypeHelper.IsSignedPrimitive(binaryOperation.LeftOperand.Type); if (binaryOperation is IEquality) { branchOp = OperationCode.Beq; if (ExpressionHelper.IsIntegralZero(binaryOperation.LeftOperand) || ExpressionHelper.IsNullLiteral(binaryOperation.LeftOperand) || binaryOperation.LeftOperand is IDefaultValue || ExpressionHelper.IsZeroIntPtr(binaryOperation.LeftOperand)) { branchOp = OperationCode.Brfalse; expression = binaryOperation.RightOperand; } else if (ExpressionHelper.IsIntegralZero(binaryOperation.RightOperand) || ExpressionHelper.IsNullLiteral(binaryOperation.RightOperand) || binaryOperation.RightOperand is IDefaultValue || ExpressionHelper.IsZeroIntPtr(binaryOperation.RightOperand)) { branchOp = OperationCode.Brfalse; expression = binaryOperation.LeftOperand; } } else if (binaryOperation is INotEquality) { branchOp = OperationCode.Bne_Un; if (ExpressionHelper.IsIntegralZero(binaryOperation.LeftOperand) || ExpressionHelper.IsNullLiteral(binaryOperation.LeftOperand) || binaryOperation.LeftOperand is IDefaultValue || ExpressionHelper.IsZeroIntPtr(binaryOperation.LeftOperand)) { branchOp = OperationCode.Brtrue; expression = binaryOperation.RightOperand; } else if (ExpressionHelper.IsIntegralZero(binaryOperation.RightOperand) || ExpressionHelper.IsNullLiteral(binaryOperation.RightOperand) || binaryOperation.RightOperand is IDefaultValue || ExpressionHelper.IsZeroIntPtr(binaryOperation.RightOperand)) { branchOp = OperationCode.Brtrue; expression = binaryOperation.LeftOperand; } } else if (binaryOperation is ILessThan) branchOp = ((ILessThan)binaryOperation).IsUnsignedOrUnordered ? OperationCode.Blt_Un : OperationCode.Blt; else if (binaryOperation is ILessThanOrEqual) branchOp = ((ILessThanOrEqual)binaryOperation).IsUnsignedOrUnordered ? OperationCode.Ble_Un : OperationCode.Ble; else if (binaryOperation is IGreaterThan) branchOp = ((IGreaterThan)binaryOperation).IsUnsignedOrUnordered ? OperationCode.Bgt_Un : OperationCode.Bgt; else if (binaryOperation is IGreaterThanOrEqual) branchOp = ((IGreaterThanOrEqual)binaryOperation).IsUnsignedOrUnordered ? OperationCode.Bge_Un : OperationCode.Bge; else { IConditional/*?*/ conditional = expression as IConditional; if (conditional != null) { ICompileTimeConstant/*?*/ resultIfFalse = conditional.ResultIfFalse as ICompileTimeConstant; if (resultIfFalse != null && (ExpressionHelper.IsIntegralZero(resultIfFalse) || (resultIfFalse.Value is bool && !((bool)resultIfFalse.Value)))) { //conditional.Condition && conditional.ResultIfTrue ILGeneratorLabel fallThrough = new ILGeneratorLabel(); this.VisitBranchIfFalse(conditional.Condition, fallThrough); this.VisitBranchIfTrue(conditional.ResultIfTrue, targetLabel); this.generator.MarkLabel(fallThrough); return; } ICompileTimeConstant/*?*/ resultIfTrue = conditional.ResultIfTrue as ICompileTimeConstant; if (resultIfTrue != null && (ExpressionHelper.IsIntegralOne(resultIfTrue) || (resultIfTrue.Value is bool && (bool)resultIfTrue.Value))) { //conditional.Condition || conditional.ResultIfFalse this.VisitBranchIfTrue(conditional.Condition, targetLabel); this.VisitBranchIfTrue(conditional.ResultIfFalse, targetLabel); return; } } IMethodCall/*?*/ methodCall = expression as IMethodCall; if (methodCall != null) { int mkey = methodCall.MethodToCall.Name.UniqueKey; if ((mkey == this.host.NameTable.OpEquality.UniqueKey || mkey == this.host.NameTable.OpInequality.UniqueKey) && TypeHelper.TypesAreEquivalent(methodCall.MethodToCall.ContainingType, methodCall.Type.PlatformType.SystemMulticastDelegate)) { List<IExpression> operands = new List<IExpression>(methodCall.Arguments); if (operands.Count == 2) { if (ExpressionHelper.IsNullLiteral(operands[0])) expression = operands[1]; else if (ExpressionHelper.IsNullLiteral(operands[1])) expression = operands[0]; if (expression != methodCall) branchOp = methodCall.MethodToCall.Name.UniqueKey == this.host.NameTable.OpEquality.UniqueKey ? OperationCode.Brfalse : OperationCode.Brtrue; } } } else { ILogicalNot/*?*/ logicalNot = expression as ILogicalNot; if (logicalNot != null) { this.VisitBranchIfFalse(logicalNot.Operand, targetLabel); return; } } } if (branchOp == OperationCode.Brfalse || branchOp == OperationCode.Brtrue) { this.Traverse(expression); this.StackSize--; } else { this.Traverse(binaryOperation.LeftOperand); this.Traverse(binaryOperation.RightOperand); this.StackSize-=2; } this.generator.Emit(branchOp, targetLabel); }
/// <summary> /// Emits the specified number of operations from the original method body. /// </summary> /// <param name="count"></param> public void EmitOperations(int count) { for (int i = 0; i < count; i++) { IOperation op = operations[0]; operations.RemoveAt(0); ILGeneratorLabel label; if (op.Location is IILLocation) MarkSequencePoint(op.Location); // Mark operation if it is a label for a branch if (offset2Label.TryGetValue(op.Offset, out label)) MarkLabel(label); // Mark operation if it is pointed to by an exception handler bool ignore; uint offset = op.Offset; if (offsetsUsedInExceptionInformation.TryGetValue(offset, out ignore)) { foreach (var exceptionInfo in methodBody.OperationExceptionInformation) { if (offset == exceptionInfo.TryStartOffset) BeginTryBody(); // Never need to do anthing when offset == exceptionInfo.TryEndOffset because // we pick up an EndTryBody from the HandlerEndOffset below // EndTryBody(); if (offset == exceptionInfo.HandlerStartOffset) { switch (exceptionInfo.HandlerKind) { case HandlerKind.Catch: BeginCatchBlock(exceptionInfo.ExceptionType); break; case HandlerKind.Fault: BeginFaultBlock(); break; case HandlerKind.Filter: BeginFilterBody(); break; case HandlerKind.Finally: BeginFinallyBlock(); break; } } if (exceptionInfo.HandlerKind == HandlerKind.Filter && offset == exceptionInfo.FilterDecisionStartOffset) BeginFilterBlock(); if (offset == exceptionInfo.HandlerEndOffset) EndTryBody(); } } // Emit operation along with any injection switch (op.OperationCode) { // Branches case OperationCode.Beq: case OperationCode.Bge: case OperationCode.Bge_Un: case OperationCode.Bgt: case OperationCode.Bgt_Un: case OperationCode.Ble: case OperationCode.Ble_Un: case OperationCode.Blt: case OperationCode.Blt_Un: case OperationCode.Bne_Un: case OperationCode.Br: case OperationCode.Brfalse: case OperationCode.Brtrue: case OperationCode.Leave: case OperationCode.Beq_S: case OperationCode.Bge_S: case OperationCode.Bge_Un_S: case OperationCode.Bgt_S: case OperationCode.Bgt_Un_S: case OperationCode.Ble_S: case OperationCode.Ble_Un_S: case OperationCode.Blt_S: case OperationCode.Blt_Un_S: case OperationCode.Bne_Un_S: case OperationCode.Br_S: case OperationCode.Brfalse_S: case OperationCode.Brtrue_S: case OperationCode.Leave_S: Emit(ILGenerator.LongVersionOf(op.OperationCode), offset2Label[(uint)op.Value]); break; case OperationCode.Switch: uint[] offsets = op.Value as uint[]; ILGeneratorLabel[] labels = new ILGeneratorLabel[offsets.Length]; for (int j = 0, n = offsets.Length; j < n; j++) { labels[j] = offset2Label[offsets[j]]; } Emit(OperationCode.Switch, labels); break; // Everything else default: if (op.Value == null) { Emit(op.OperationCode); break; } var typeCode = System.Convert.GetTypeCode(op.Value); switch (typeCode) { case TypeCode.Byte: Emit(op.OperationCode, (byte)op.Value); break; case TypeCode.Double: Emit(op.OperationCode, (double)op.Value); break; case TypeCode.Int16: Emit(op.OperationCode, (short)op.Value); break; case TypeCode.Int32: Emit(op.OperationCode, (int)op.Value); break; case TypeCode.Int64: Emit(op.OperationCode, (long)op.Value); break; case TypeCode.Object: IFieldReference fieldReference = op.Value as IFieldReference; if (fieldReference != null) { Emit(op.OperationCode, fieldReference); break; } ILocalDefinition localDefinition = op.Value as ILocalDefinition; if (localDefinition != null) { Emit(op.OperationCode, localDefinition); break; } IMethodReference methodReference = op.Value as IMethodReference; if (methodReference != null) { Emit(op.OperationCode, methodReference); break; } IParameterDefinition parameterDefinition = op.Value as IParameterDefinition; if (parameterDefinition != null) { Emit(op.OperationCode, parameterDefinition); break; } ISignature signature = op.Value as ISignature; if (signature != null) { Emit(op.OperationCode, signature); break; } ITypeReference typeReference = op.Value as ITypeReference; if (typeReference != null) { Emit(op.OperationCode, typeReference); break; } throw new Exception("Should never get here: no other IOperation argument types should exist"); case TypeCode.SByte: Emit(op.OperationCode, (sbyte)op.Value); break; case TypeCode.Single: Emit(op.OperationCode, (float)op.Value); break; case TypeCode.String: Emit(op.OperationCode, (string)op.Value); break; default: throw new Exception("Should never get here: no other IOperation argument types should exist"); } break; } } }
/// <summary> /// This method is called for all opcodes related to branches: /// OperationCode.Beq: /// OperationCode.Bge: /// OperationCode.Bge_Un: /// OperationCode.Bgt: /// OperationCode.Bgt_Un: /// OperationCode.Ble: /// OperationCode.Ble_Un: /// OperationCode.Blt: /// OperationCode.Blt_Un: /// OperationCode.Bne_Un: /// OperationCode.Br: /// OperationCode.Brfalse: /// OperationCode.Brtrue: /// OperationCode.Leave: /// OperationCode.Beq_S: /// OperationCode.Bge_S: /// OperationCode.Bge_Un_S: /// OperationCode.Bgt_S: /// OperationCode.Bgt_Un_S: /// OperationCode.Ble_S: /// OperationCode.Ble_Un_S: /// OperationCode.Blt_S: /// OperationCode.Blt_Un_S: /// OperationCode.Bne_Un_S: /// OperationCode.Br_S: /// OperationCode.Brfalse_S: /// OperationCode.Brtrue_S: /// OperationCode.Leave_S: /// </summary> /// <param name="op">The Microsoft intermediate language (MSIL) instruction to be copied.</param> protected virtual void RewriteBranch(IOperation op) { Contract.Requires(this.generator != null); Contract.Requires(op != null); // handle branches switch (op.OperationCode) { // Branches case OperationCode.Beq: case OperationCode.Bge: case OperationCode.Bge_Un: case OperationCode.Bgt: case OperationCode.Bgt_Un: case OperationCode.Ble: case OperationCode.Ble_Un: case OperationCode.Blt: case OperationCode.Blt_Un: case OperationCode.Bne_Un: case OperationCode.Br: case OperationCode.Brfalse: case OperationCode.Brtrue: case OperationCode.Leave: case OperationCode.Beq_S: case OperationCode.Bge_S: case OperationCode.Bge_Un_S: case OperationCode.Bgt_S: case OperationCode.Bgt_Un_S: case OperationCode.Ble_S: case OperationCode.Ble_Un_S: case OperationCode.Blt_S: case OperationCode.Blt_Un_S: case OperationCode.Bne_Un_S: case OperationCode.Br_S: case OperationCode.Brfalse_S: case OperationCode.Brtrue_S: case OperationCode.Leave_S: this.generator.Emit(ILGenerator.LongVersionOf(op.OperationCode), this.offset2Label[(uint)op.Value]); break; case OperationCode.Switch: uint[] offsets = op.Value as uint[]; ILGeneratorLabel[] labels = new ILGeneratorLabel[offsets.Length]; for (int j = 0, n = offsets.Length; j < n; j++) { labels[j] = offset2Label[offsets[j]]; } this.generator.Emit(OperationCode.Switch, labels); break; default: throw new Exception("Only branches should be handled here"); } }