public static VariableSlot[] CloneVariableState(VariableSlot[] state) { var clone = new VariableSlot[state.Length]; Array.Copy(state, clone, state.Length); return(clone); }
public static VariableSlot[] MakeFullState(int varCount) { VariableSlot[] unknownVariableState = new VariableSlot[varCount]; for (int i = 0; i < unknownVariableState.Length; i++) { unknownVariableState[i] = new VariableSlot(Empty.Array<ByteCode>(), true); } return unknownVariableState; }
public static VariableSlot[] MakeEmptyState(int varCount) { VariableSlot[] emptyVariableState = new VariableSlot[varCount]; for (int i = 0; i < emptyVariableState.Length; i++) { emptyVariableState[i] = new VariableSlot(Empty.Array<ByteCode>(), false); } return emptyVariableState; }
public static VariableSlot[] CloneVariableState(VariableSlot[] state) { VariableSlot[] clone = new VariableSlot[state.Length]; for (int i = 0; i < clone.Length; i++) { clone[i] = state[i]; } return clone; }
public static VariableSlot[] MakeEmptyState(int varCount) { VariableSlot[] emptyVariableState = new VariableSlot[varCount]; for (int i = 0; i < emptyVariableState.Length; i++) { emptyVariableState[i] = new VariableSlot(EmptyByteCodeArray, false); } return(emptyVariableState); }
public static VariableSlot[] CloneVariableState(VariableSlot[] state) { VariableSlot[] clone = new VariableSlot[state.Length]; for (int i = 0; i < clone.Length; i++) { clone[i] = state[i]; } return(clone); }
public static VariableSlot[] MakeFullState(int varCount) { VariableSlot[] unknownVariableState = new VariableSlot[varCount]; for (int i = 0; i < unknownVariableState.Length; i++) { unknownVariableState[i] = new VariableSlot(EmptyByteCodeArray, true); } return(unknownVariableState); }
public static VariableSlot[] MakeUknownState(int varCount) { VariableSlot[] unknownVariableState = new VariableSlot[varCount]; for (int i = 0; i < unknownVariableState.Length; i++) { unknownVariableState[i] = UnknownInstance; } return(unknownVariableState); }
public static VariableSlot[] MakeUnknownState(int varCount) { var unknownVariableState = new VariableSlot[varCount]; for (int i = 0; i < unknownVariableState.Length; i++) { unknownVariableState[i] = UnknownInstance; } return unknownVariableState; }
List <ByteCode> StackAnalysis(MethodDefinition methodDef) { Dictionary <Instruction, ByteCode> instrToByteCode = new Dictionary <Instruction, ByteCode>(); // Create temporary structure for the stack analysis List <ByteCode> body = new List <ByteCode>(methodDef.Body.Instructions.Count); List <Instruction> prefixes = null; foreach (Instruction inst in methodDef.Body.Instructions) { if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { if (prefixes == null) { prefixes = new List <Instruction>(1); } prefixes.Add(inst); continue; } ILCode code = (ILCode)inst.OpCode.Code; object operand = inst.Operand; ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body); ByteCode byteCode = new ByteCode() { Offset = inst.Offset, EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize, Code = code, Operand = operand, PopCount = inst.GetPopDelta(methodDef), PushCount = inst.GetPushDelta() }; if (prefixes != null) { instrToByteCode[prefixes[0]] = byteCode; byteCode.Offset = prefixes[0].Offset; byteCode.Prefixes = prefixes.ToArray(); prefixes = null; } else { instrToByteCode[inst] = byteCode; } body.Add(byteCode); } for (int i = 0; i < body.Count - 1; i++) { body[i].Next = body[i + 1]; } Stack <ByteCode> agenda = new Stack <ByteCode>(); int varCount = methodDef.Body.Variables.Count; var exceptionHandlerStarts = new HashSet <ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart])); // Add known states if (methodDef.Body.HasExceptionHandlers) { foreach (ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { ByteCode handlerStart = instrToByteCode[ex.HandlerStart]; handlerStart.StackBefore = new List <StackSlot>(); handlerStart.VariablesBefore = VariableSlot.MakeFullState(varCount); if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { // Catch and Filter handlers start with the exeption on the stack ByteCode ldexception = new ByteCode() { Code = ILCode.Ldexception, Operand = ex.CatchType, PopCount = 0, PushCount = 1 }; ldexceptions[ex] = ldexception; handlerStart.StackBefore.Add(new StackSlot(ldexception)); } agenda.Push(handlerStart); if (ex.HandlerType == ExceptionHandlerType.Filter) { ByteCode filterStart = instrToByteCode[ex.FilterStart]; filterStart.StackBefore = new List <StackSlot>(); filterStart.VariablesBefore = VariableSlot.MakeFullState(varCount); ByteCode ldexception = new ByteCode() { Code = ILCode.Ldexception, Operand = ex.CatchType, PopCount = 0, PushCount = 1 }; // TODO: ldexceptions[ex] = ldexception; filterStart.StackBefore.Add(new StackSlot(ldexception)); agenda.Push(filterStart); } } } body[0].StackBefore = new List <StackSlot>(); body[0].VariablesBefore = VariableSlot.MakeEmptyState(varCount); agenda.Push(body[0]); // Process agenda while (agenda.Count > 0) { ByteCode byteCode = agenda.Pop(); // Calculate new stack List <StackSlot> newStack = StackSlot.CloneStack(byteCode.StackBefore, byteCode.PopCount); for (int i = 0; i < byteCode.PushCount; i++) { newStack.Add(new StackSlot(byteCode)); } // Calculate new variable state VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); if (byteCode.Code == ILCode.Stloc) { int varIndex = ((VariableReference)byteCode.Operand).Index; newVariableState[varIndex] = new VariableSlot(byteCode); } // After the leave, finally block might have touched the variables if (byteCode.Code == ILCode.Leave) { newVariableState = VariableSlot.MakeFullState(varCount); } // Find all successors List <ByteCode> branchTargets = new List <ByteCode>(); if (!byteCode.Code.IsUnconditionalControlFlow()) { if (exceptionHandlerStarts.Contains(byteCode.Next)) { // Do not fall though down to exception handler // It is invalid IL as per ECMA-335 ยง12.4.2.8.1, but some obfuscators produce it } else { branchTargets.Add(byteCode.Next); } } if (byteCode.Operand is Instruction[]) { foreach (Instruction inst in (Instruction[])byteCode.Operand) { ByteCode target = instrToByteCode[inst]; branchTargets.Add(target); // The target of a branch must have label if (target.Label == null) { target.Label = new ILLabel() { Name = target.Name }; } } } else if (byteCode.Operand is Instruction) { ByteCode target = instrToByteCode[(Instruction)byteCode.Operand]; branchTargets.Add(target); // The target of a branch must have label if (target.Label == null) { target.Label = new ILLabel() { Name = target.Name }; } } // Apply the state to successors foreach (ByteCode branchTarget in branchTargets) { if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) { if (branchTargets.Count == 1) { branchTarget.StackBefore = newStack; branchTarget.VariablesBefore = newVariableState; } else { // Do not share data for several bytecodes branchTarget.StackBefore = StackSlot.CloneStack(newStack, 0); branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); } agenda.Push(branchTarget); } else { if (branchTarget.StackBefore.Count != newStack.Count) { throw new Exception("Inconsistent stack size at " + byteCode.Name); } // Be careful not to change our new data - it might be reused for several branch targets. // In general, be careful that two bytecodes never share data structures. bool modified = false; // Merge stacks - modify the target for (int i = 0; i < newStack.Count; i++) { ByteCode[] oldPushedBy = branchTarget.StackBefore[i].PushedBy; ByteCode[] newPushedBy = oldPushedBy.Union(newStack[i].PushedBy); if (newPushedBy.Length > oldPushedBy.Length) { branchTarget.StackBefore[i] = new StackSlot(newPushedBy, null); modified = true; } } // Merge variables - modify the target for (int i = 0; i < newVariableState.Length; i++) { VariableSlot oldSlot = branchTarget.VariablesBefore[i]; VariableSlot newSlot = newVariableState[i]; // All can not be unioned further if (!oldSlot.StoredByAll) { if (newSlot.StoredByAll) { branchTarget.VariablesBefore[i] = newSlot; modified = true; } else { ByteCode[] oldStoredBy = oldSlot.StoredBy; ByteCode[] newStoredBy = oldStoredBy.Union(newSlot.StoredBy); if (newStoredBy.Length > oldStoredBy.Length) { branchTarget.VariablesBefore[i] = new VariableSlot(newStoredBy, false); modified = true; } } } } if (modified) { agenda.Push(branchTarget); } } } } // Occasionally the compilers or obfuscators generate unreachable code (which migt be intentonally invalid) // I belive it is safe to just remove it body.RemoveAll(b => b.StackBefore == null); // Genertate temporary variables to replace stack foreach (ByteCode byteCode in body) { int argIdx = 0; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true }; byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].PushedBy, tmpVar); foreach (ByteCode pushedBy in byteCode.StackBefore[i].PushedBy) { if (pushedBy.StoreTo == null) { pushedBy.StoreTo = new List <ILVariable>(1); } pushedBy.StoreTo.Add(tmpVar); } argIdx++; } } // Try to use single temporary variable insted of several if possilbe (especially useful for dup) // This has to be done after all temporary variables are assigned so we know about all loads foreach (ByteCode byteCode in body) { if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { var locVars = byteCode.StoreTo; // For each of the variables, find the location where it is loaded - there should be preciesly one var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); // We now know that all the variables have a single load, // Let's make sure that they have also a single store - us if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) { // Great - we can reduce everything into single variable ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true }; byteCode.StoreTo = new List <ILVariable>() { tmpVar }; foreach (ByteCode bc in body) { for (int i = 0; i < bc.StackBefore.Count; i++) { // Is it one of the variable to be merged? if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { // Replace with the new temp variable bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].PushedBy, tmpVar); } } } } } } // Split and convert the normal local variables ConvertLocalVariables(body); // Convert branch targets to labels foreach (ByteCode byteCode in body) { if (byteCode.Operand is Instruction[]) { List <ILLabel> newOperand = new List <ILLabel>(); foreach (Instruction target in (Instruction[])byteCode.Operand) { newOperand.Add(instrToByteCode[target].Label); } byteCode.Operand = newOperand.ToArray(); } else if (byteCode.Operand is Instruction) { byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label; } } // Convert parameters to ILVariables ConvertParameters(body); return(body); }
public static VariableSlot[] CloneVariableState(VariableSlot[] state) { var clone = new VariableSlot[state.Length]; Array.Copy(state, clone, state.Length); return clone; }
/// <summary> /// Update the variable state of handlers for the exception handlers that start at the given branch target. /// </summary> private void UpdateTryStartBranchTarget(ByteCode branchTarget, IEnumerable<ExceptionHandler> exceptionHandlers, Dictionary<Instruction, ByteCode> instrToByteCode, VariableSlot[] newVariableState, Stack<ByteCode> agenda) { // The branch target is the start of a try block. // Collection all exception handler that share the same handler as this try block. var ehs = new HashSet<ExceptionHandler>(exceptionHandlers); foreach (var eh in validExceptionHandlers.Where(x => ehs.Any(y => y.HandlerPc == x.HandlerPc))) { ehs.Add(eh); } // Collection the variables defined in all try block's of the collected exception handlers. var defs = new HashSet<LocalVariableReference>(); foreach (var eh in ehs) { GetDefinedVariables(instrToByteCode[eh.Start], instrToByteCode[eh.End], defs); } // Merge variables of handlers - modify the target foreach (var handler in ehs.Select(x => instrToByteCode[x.Handler])) { var modified = false; for (var i = 0; i < newVariableState.Length; i++) { // Update variables unless it is defined if (defs.Any(x => x.Index == i)) continue; var oldSlot = handler.VariablesBefore[i]; var newSlot = newVariableState[i]; if (newSlot.UnknownDefinition) { if (!oldSlot.UnknownDefinition) { handler.VariablesBefore[i] = newSlot; modified = true; } } else { var oldDefs = oldSlot.Definitions; var newDefs = oldDefs.Union(newSlot.Definitions); if (newDefs.Length > oldDefs.Length) { handler.VariablesBefore[i] = new VariableSlot(newDefs, false); modified = true; } } } if (modified) { agenda.Push(handler); } } }
/// <summary> /// Update the state of the given branch targets (of the given bytecode). /// Add modified branch targets to the given agenda. /// </summary> private static void UpdateBranchTarget(ByteCode byteCode, ByteCode branchTarget, bool canShareNewState, StackSlot[] newStack, VariableSlot[] newVariableState, Stack<ByteCode> agenda) { if ((branchTarget.StackBefore == null) && (branchTarget.VariablesBefore == null)) { // Branch target has not been processed at all if (canShareNewState) { branchTarget.StackBefore = newStack; branchTarget.VariablesBefore = newVariableState; } else { // Do not share data for several bytecodes branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null); branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); } agenda.Push(branchTarget); return; } // If we get here, the branch target has been processed before. // See the the stack size is the same and merge the state where needed. if ((branchTarget.StackBefore == null) || (branchTarget.StackBefore.Length != newStack.Length)) { throw new Exception("Inconsistent stack size at " + byteCode.Name); } // Be careful not to change our new data - it might be reused for several branch targets. // In general, be careful that two bytecodes never share data structures. var modified = false; // Merge stacks - modify the target for (var i = 0; i < newStack.Length; i++) { var oldDefs = branchTarget.StackBefore[i].Definitions; var newDefs = oldDefs.Union(newStack[i].Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.StackBefore[i] = new StackSlot(newDefs, null); modified = true; } } // Merge variables - modify the target for (var i = 0; i < newVariableState.Length; i++) { var oldSlot = branchTarget.VariablesBefore[i]; var newSlot = newVariableState[i]; if (oldSlot.UnknownDefinition) continue; if (newSlot.UnknownDefinition) { branchTarget.VariablesBefore[i] = newSlot; modified = true; } else { var oldDefs = oldSlot.Definitions; var newDefs = oldDefs.Union(newSlot.Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false); modified = true; } } } if (modified) { agenda.Push(branchTarget); } }
/// <summary> /// Update the state of the given branch targets (of the given bytecode). /// Add modified branch targets to the given agenda. /// </summary> private static void UpdateBranchTarget(ByteCode byteCode, ByteCode branchTarget, bool canShareNewState, StackSlot[] newStack, VariableSlot[] newVariableState, Stack <ByteCode> agenda) { if ((branchTarget.StackBefore == null) && (branchTarget.VariablesBefore == null)) { // Branch target has not been processed at all if (canShareNewState) { branchTarget.StackBefore = newStack; branchTarget.VariablesBefore = newVariableState; } else { // Do not share data for several bytecodes branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null); branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); } agenda.Push(branchTarget); return; } // If we get here, the branch target has been processed before. // See the the stack size is the same and merge the state where needed. if ((branchTarget.StackBefore == null) || (branchTarget.StackBefore.Length != newStack.Length)) { throw new Exception("Inconsistent stack size at " + byteCode.Name); } // Be careful not to change our new data - it might be reused for several branch targets. // In general, be careful that two bytecodes never share data structures. var modified = false; // Merge stacks - modify the target for (var i = 0; i < newStack.Length; i++) { var oldDefs = branchTarget.StackBefore[i].Definitions; var newDefs = oldDefs.Union(newStack[i].Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.StackBefore[i] = new StackSlot(newDefs, null); modified = true; } } // Merge variables - modify the target for (var i = 0; i < newVariableState.Length; i++) { var oldSlot = branchTarget.VariablesBefore[i]; var newSlot = newVariableState[i]; if (oldSlot.UnknownDefinition) { continue; } if (newSlot.UnknownDefinition) { branchTarget.VariablesBefore[i] = newSlot; modified = true; } else { var oldDefs = oldSlot.Definitions; var newDefs = oldDefs.Union(newSlot.Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false); modified = true; } } } if (modified) { agenda.Push(branchTarget); } }
/// <summary> /// Analyse the instructions in the method code and convert them to a ByteCode list. /// </summary> private List <ByteCode> StackAnalysis() { // Map from instruction to bytecode. var instrToByteCode = new Dictionary <Instruction, ByteCode>(); // Create temporary structure for the stack analysis var body = new List <ByteCode>(codeAttr.Code.Length); foreach (var inst in codeAttr.Instructions) { var first = true; foreach (var byteCode in Create(inst, module)) { if (first) { instrToByteCode[inst] = byteCode; first = false; } body.Add(byteCode); } } // Connect bytecodes to the next for (var i = 0; i < body.Count - 1; i++) { body[i].Next = body[i + 1]; } var agenda = new Stack <ByteCode>(); var localVarsCount = codeAttr.MaxLocals; // methodDef.GetParametersLocalVariableSlots(); // All bytecodes that are the start of an exception handler. var exceptionHandlerStarts = new HashSet <ByteCode>(validExceptionHandlers.Select(eh => instrToByteCode[eh.Handler])); var exceptionTryStarts = new DefaultDictionary <ByteCode, List <ExceptionHandler> >(x => new List <ExceptionHandler>()); foreach (var eh in validExceptionHandlers) { exceptionTryStarts[instrToByteCode[eh.Start]].Add(eh); } // Add known states var ldExceptionByHandlerPc = new Dictionary <int, ByteCode>(); foreach (var ex in validExceptionHandlers) { ByteCode ldexception; if (ldExceptionByHandlerPc.TryGetValue(ex.HandlerPc, out ldexception)) { // Re-use ldexception (that we've created for the same handler PC for another exception handler before) } else { // No handler at handlerPc processed before, do that now var handlerStart = instrToByteCode[ex.Handler]; handlerStart.StackBefore = new StackSlot[0]; handlerStart.VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount); { // Catch handlers start with the exeption on the stack ldexception = new ByteCode { Code = AstCode.Ldexception, Operand = ex.CatchType, PopCount = 0, PushCount = 1, Offset = handlerStart.Offset, Next = handlerStart, StackBefore = new StackSlot[0], VariablesBefore = handlerStart.VariablesBefore }; handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) }; } ldExceptionByHandlerPc[ex.HandlerPc] = ldexception; agenda.Push(handlerStart); } // Store ldexception by exception handler ldexceptions[ex] = ldexception; } // At the start of the method the stack is empty and all local variables have unknown state body[0].StackBefore = new StackSlot[0]; body[0].VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount); agenda.Push(body[0]); // Process agenda while (agenda.Count > 0) { var byteCode = agenda.Pop(); // Calculate new stack var newStack = byteCode.CreateNewStack(); // Calculate new variable state var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); if (byteCode.IsVariableDefinition) { newVariableState[((LocalVariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false); } // After the leave, finally block might have touched the variables if (byteCode.Code == AstCode.Leave) { newVariableState = VariableSlot.MakeUnknownState(localVarsCount); } // Find all successors var branchTargets = FindBranchTargets(byteCode, instrToByteCode, exceptionHandlerStarts); // Apply the state to successors foreach (var branchTarget in branchTargets) { UpdateBranchTarget(byteCode, branchTarget, (branchTargets.Count == 1), newStack, newVariableState, agenda); } // Apply state to handlers when a branch target is the start of an exception handler foreach (var branchTarget in branchTargets.Where(exceptionTryStarts.ContainsKey)) { // The branch target is the start of a try block. UpdateTryStartBranchTarget(branchTarget, exceptionTryStarts[branchTarget], instrToByteCode, newVariableState, agenda); } } // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid) // I believe it is safe to just remove it body.RemoveAll(b => b.StackBefore == null); // Generate temporary variables to replace stack foreach (var byteCode in body) { var argIdx = 0; var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null); byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar); foreach (var pushedBy in byteCode.StackBefore[i].Definitions) { if (pushedBy.StoreTo == null) { pushedBy.StoreTo = new List <AstVariable>(1); } pushedBy.StoreTo.Add(tmpVar); } argIdx++; } } // Try to use single temporary variable insted of several if possible (especially useful for dup) // This has to be done after all temporary variables are assigned so we know about all loads foreach (var byteCode in body) { if ((byteCode.StoreTo == null) || (byteCode.StoreTo.Count <= 1)) { continue; } var locVars = byteCode.StoreTo; // For each of the variables, find the location where it is loaded - there should be preciesly one var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); // We now know that all the variables have a single load, // Let's make sure that they have also a single store - us if (loadedBy.All(slot => (slot.Definitions.Length == 1) && (slot.Definitions[0] == byteCode))) { // Great - we can reduce everything into single variable var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault()); byteCode.StoreTo = new List <AstVariable> { tmpVar }; foreach (var bc in body) { for (var i = 0; i < bc.StackBefore.Length; i++) { // Is it one of the variable to be merged? if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { // Replace with the new temp variable bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar); } } } } } // Split and convert the normal local variables ConvertLocalVariables(body); // Convert branch targets to labels foreach (var byteCode in body) { if (byteCode.Operand is Instruction[]) { byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label(true)).ToArray(); } else if (byteCode.Operand is Instruction) { byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label(true); } else if (byteCode.Operand is LookupSwitchData) { var data = (LookupSwitchData)byteCode.Operand; byteCode.Operand = data.Pairs.Select(x => new AstLabelKeyPair(instrToByteCode[x.Target].Label(true), x.Match)).ToArray(); } } // Convert parameters to ILVariables ConvertParameters(body); // Replace temporary opcodes foreach (var byteCode in body) { switch (byteCode.Code) { case AstCode.Dup_x1: case AstCode.Dup_x2: case AstCode.Dup2: case AstCode.Dup2_x1: case AstCode.Dup2_x2: case AstCode.Swap: byteCode.Code = AstCode.Dup; break; case AstCode.Pop2: byteCode.Code = AstCode.Pop; break; } } return(body); }