private SetupStatusEnum LoadOpCodes(string path, out OpCodeWrapper opCodes) { FileInfo fileInfo = new FileInfo(path); SetupStatusEnum status = SetupStatusEnum.Idle; opCodes = null; try { if (fileInfo.Exists) { opCodes = LoadFile(path) as OpCodeWrapper; if (opCodes == null) { status = SetupStatusEnum.Corrupt; } else if (opCodes.CompatibleWith(typeof(T))) { status = SetupStatusEnum.Valid; } else { status = SetupStatusEnum.Incompatible; } } else { status = SetupStatusEnum.Missing; } } catch (System.Exception) { status = SetupStatusEnum.Corrupt; } return(status); }
private void OptimizePatterns() { SetLabelRefCounts(); for (int i = 1; i < code.Count; i++) { if (code[i].opcode == OpCodes.Isinst && code[i + 1].opcode == OpCodes.Ldnull && code[i + 2].opcode == OpCodes.Cgt_Un && (code[i + 3].opcode == OpCodes.Brfalse || code[i + 3].opcode == OpCodes.Brtrue)) { code.RemoveRange(i + 1, 2); } else if (code[i].opcode == OpCodes.Ldelem_I1 && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 255 && code[i + 2].opcode == OpCodes.And) { code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null); code.RemoveRange(i + 1, 2); } else if (code[i].opcode == OpCodes.Ldelem_I1 && code[i + 1].opcode == OpCodes.Conv_I8 && code[i + 2].opcode == OpCodes.Ldc_I8 && code[i + 2].ValueInt64 == 255 && code[i + 3].opcode == OpCodes.And) { code[i] = new OpCodeWrapper(OpCodes.Ldelem_U1, null); code.RemoveRange(i + 2, 2); } else if (code[i].opcode == OpCodes.Ldc_I4 && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 2].opcode == OpCodes.And) { code[i] = new OpCodeWrapper(OpCodes.Ldc_I4, code[i].ValueInt32 & code[i + 1].ValueInt32); code.RemoveRange(i + 1, 2); } else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, Types.Double) // dcmpl || MatchCompare(i, OpCodes.Cgt, OpCodes.Clt_Un, Types.Single)) // fcmpl { PatchCompare(i, OpCodes.Ble_Un, OpCodes.Blt_Un, OpCodes.Bge, OpCodes.Bgt); } else if (MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, Types.Double) // dcmpg || MatchCompare(i, OpCodes.Cgt_Un, OpCodes.Clt, Types.Single)) // fcmpg { PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge_Un, OpCodes.Bgt_Un); } else if (MatchCompare(i, OpCodes.Cgt, OpCodes.Clt, Types.Int64)) // lcmp { PatchCompare(i, OpCodes.Ble, OpCodes.Blt, OpCodes.Bge, OpCodes.Bgt); } else if (i < code.Count - 10 && code[i].opcode == OpCodes.Ldc_I4 && code[i + 1].opcode == OpCodes.Dup && code[i + 2].opcode == OpCodes.Ldc_I4_M1 && code[i + 3].opcode == OpCodes.Bne_Un && code[i + 4].opcode == OpCodes.Pop && code[i + 5].opcode == OpCodes.Neg && code[i + 6].opcode == OpCodes.Br && code[i + 7].pseudo == CodeType.Label && code[i + 7].MatchLabel(code[i + 3]) && code[i + 7].Label.Temp == 1 && code[i + 8].opcode == OpCodes.Div && code[i + 9].pseudo == CodeType.Label && code[i + 9].Label == code[i + 6].Label && code[i + 9].Label.Temp == 1) { int divisor = code[i].ValueInt32; if (divisor == -1) { code[i] = code[i + 5]; code.RemoveRange(i + 1, 9); } else { code[i + 1] = code[i + 8]; code.RemoveRange(i + 2, 8); } } else if (i < code.Count - 11 && code[i].opcode == OpCodes.Ldc_I8 && code[i + 1].opcode == OpCodes.Dup && code[i + 2].opcode == OpCodes.Ldc_I4_M1 && code[i + 3].opcode == OpCodes.Conv_I8 && code[i + 4].opcode == OpCodes.Bne_Un && code[i + 5].opcode == OpCodes.Pop && code[i + 6].opcode == OpCodes.Neg && code[i + 7].opcode == OpCodes.Br && code[i + 8].pseudo == CodeType.Label && code[i + 8].MatchLabel(code[i + 4]) && code[i + 8].Label.Temp == 1 && code[i + 9].opcode == OpCodes.Div && code[i + 10].pseudo == CodeType.Label && code[i + 10].MatchLabel(code[i + 7]) && code[i + 10].Label.Temp == 1) { long divisor = code[i].ValueInt64; if (divisor == -1) { code[i] = code[i + 6]; code.RemoveRange(i + 1, 10); } else { code[i + 1] = code[i + 9]; code.RemoveRange(i + 2, 9); } } else if (code[i].opcode == OpCodes.Box && code[i + 1].opcode == OpCodes.Unbox && code[i + 1].Type == code[i].Type) { CodeEmitterLocal local = new CodeEmitterLocal(code[i].Type); code[i] = new OpCodeWrapper(OpCodes.Stloc, local); code[i + 1] = new OpCodeWrapper(OpCodes.Ldloca, local); } else if (i < code.Count - 13 && code[i + 0].opcode == OpCodes.Box && code[i + 1].opcode == OpCodes.Dup && code[i + 2].opcode == OpCodes.Brtrue && code[i + 3].opcode == OpCodes.Pop && code[i + 4].opcode == OpCodes.Ldloca && code[i + 4].Local.LocalType == code[i + 0].Type && code[i + 5].opcode == OpCodes.Initobj && code[i + 5].Type == code[i + 0].Type && code[i + 6].opcode == OpCodes.Ldloc && code[i + 6].Local == code[i + 4].Local && code[i + 7].pseudo == CodeType.ReleaseTempLocal && code[i + 7].Local == code[i + 6].Local && code[i + 8].opcode == OpCodes.Br && code[i + 9].pseudo == CodeType.Label && code[i + 9].MatchLabel(code[i + 2]) && code[i + 9].Label.Temp == 1 && code[i + 10].opcode == OpCodes.Unbox && code[i + 10].Type == code[i + 0].Type && code[i + 11].opcode == OpCodes.Ldobj && code[i + 11].Type == code[i + 0].Type && code[i + 12].pseudo == CodeType.Label && code[i + 12].MatchLabel(code[i + 8]) && code[i + 12].Label.Temp == 1) { code.RemoveRange(i, 13); } // NOTE intentionally not an else, because we want to optimize the code generated by the earlier compare optimization if (i < code.Count - 6 && code[i].opcode.FlowControl == FlowControl.Cond_Branch && code[i + 1].opcode == OpCodes.Ldc_I4 && code[i + 1].ValueInt32 == 1 && code[i + 2].opcode == OpCodes.Br && code[i + 3].pseudo == CodeType.Label && code[i + 3].MatchLabel(code[i]) && code[i + 3].Label.Temp == 1 && code[i + 4].opcode == OpCodes.Ldc_I4 && code[i + 4].ValueInt32 == 0 && code[i + 5].pseudo == CodeType.Label && code[i + 5].MatchLabel(code[i + 2]) && code[i + 5].Label.Temp == 1) { if (code[i].opcode == OpCodes.Bne_Un) { code[i] = new OpCodeWrapper(OpCodes.Ceq, null); code.RemoveRange(i + 1, 5); } else if (code[i].opcode == OpCodes.Beq) { code[i + 0] = new OpCodeWrapper(OpCodes.Ceq, null); code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); code.RemoveRange(i + 3, 3); } else if (code[i].opcode == OpCodes.Ble || code[i].opcode == OpCodes.Ble_Un) { code[i] = new OpCodeWrapper(OpCodes.Cgt, null); code.RemoveRange(i + 1, 5); } else if (code[i].opcode == OpCodes.Blt) { code[i] = new OpCodeWrapper(OpCodes.Clt, null); code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); code.RemoveRange(i + 3, 3); } else if (code[i].opcode == OpCodes.Blt_Un) { code[i] = new OpCodeWrapper(OpCodes.Clt_Un, null); code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); code.RemoveRange(i + 3, 3); } else if (code[i].opcode == OpCodes.Bge || code[i].opcode == OpCodes.Bge_Un) { code[i] = new OpCodeWrapper(OpCodes.Clt, null); code.RemoveRange(i + 1, 5); } else if (code[i].opcode == OpCodes.Bgt) { code[i] = new OpCodeWrapper(OpCodes.Cgt, null); code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); code.RemoveRange(i + 3, 3); } else if (code[i].opcode == OpCodes.Bgt_Un) { code[i] = new OpCodeWrapper(OpCodes.Cgt_Un, null); code[i + 1] = new OpCodeWrapper(OpCodes.Ldc_I4, 0); code[i + 2] = new OpCodeWrapper(OpCodes.Ceq, null); code.RemoveRange(i + 3, 3); } } } }
private void OptimizeBranchSizes() { int offset = 0; for (int i = 0; i < code.Count; i++) { if (code[i].pseudo == CodeType.Label) { code[i].Label.Temp = offset; } offset += code[i].Size; } offset = 0; for (int i = 0; i < code.Count; i++) { int prevOffset = offset; offset += code[i].Size; if (code[i].HasLabel && code[i].opcode.OperandType == OperandType.InlineBrTarget) { CodeEmitterLabel label = code[i].Label; int diff = label.Temp - (prevOffset + code[i].opcode.Size + 1); if (-128 <= diff && diff <= 127) { OpCode opcode = code[i].opcode; if (opcode == OpCodes.Brtrue) { opcode = OpCodes.Brtrue_S; } else if (opcode == OpCodes.Brfalse) { opcode = OpCodes.Brfalse_S; } else if (opcode == OpCodes.Br) { opcode = OpCodes.Br_S; } else if (opcode == OpCodes.Beq) { opcode = OpCodes.Beq_S; } else if (opcode == OpCodes.Bne_Un) { opcode = OpCodes.Bne_Un_S; } else if (opcode == OpCodes.Ble) { opcode = OpCodes.Ble_S; } else if (opcode == OpCodes.Ble_Un) { opcode = OpCodes.Ble_Un_S; } else if (opcode == OpCodes.Blt) { opcode = OpCodes.Blt_S; } else if (opcode == OpCodes.Blt_Un) { opcode = OpCodes.Blt_Un_S; } else if (opcode == OpCodes.Bge) { opcode = OpCodes.Bge_S; } else if (opcode == OpCodes.Bge_Un) { opcode = OpCodes.Bge_Un_S; } else if (opcode == OpCodes.Bgt) { opcode = OpCodes.Bgt_S; } else if (opcode == OpCodes.Bgt_Un) { opcode = OpCodes.Bgt_Un_S; } else if (opcode == OpCodes.Leave) { opcode = OpCodes.Leave_S; } code[i] = new OpCodeWrapper(opcode, label); } } } }
private void AnnihilateStoreReleaseTempLocals() { for (int i = 1; i < code.Count; i++) { if (code[i].opcode == OpCodes.Stloc) { if (code[i + 1].pseudo == CodeType.ReleaseTempLocal && code[i].Local == code[i + 1].Local) { code[i] = new OpCodeWrapper(OpCodes.Pop, null); } else if (code[i + 1].opcode == OpCodes.Ldloc && code[i + 1].Local == code[i].Local && code[i + 2].pseudo == CodeType.ReleaseTempLocal && code[i + 2].Local == code[i].Local) { code.RemoveRange(i, 2); } } } }
internal bool MatchLocal(OpCodeWrapper other) { return data == other.data; }
private void CLRv4_x64_JIT_Workaround() { for (int i = 0; i < code.Count - 2; i++) { // This is a workaround for https://connect.microsoft.com/VisualStudio/feedback/details/566946/x64-jit-optimization-bug // // Testing shows that the bug appears to be very specific and requires a comparison of a method argument with zero. // For example, the problem goes away when the method argument is first assigned to a local variable and then // the comparison (and subsequent use) is done against the local variable. // // This means we only have to detect these specific patterns: // // ldc.i8 0x0 ldarg // ldarg ldc.i8 0x0 // beq/bne beq/bne // // The workaround is to replace ldarg with ldarga/ldind.i8. Looking at the generated code by the x86 and x64 JITs // this appears to be as efficient as the ldarg and it avoids the x64 bug. if (code[i].opcode == OpCodes.Ldc_I8 && code[i].ValueInt64 == 0) { short arg; int m; if (i > 0 && MatchLdarg(code[i - 1], out arg) && IsBranchEqNe(code[i + 1].opcode)) { m = i - 1; } else if (MatchLdarg(code[i + 1], out arg) && IsBranchEqNe(code[i + 2].opcode)) { m = i + 1; } else { continue; } code[m] = new OpCodeWrapper(OpCodes.Ldarga, arg); code.Insert(m + 1, new OpCodeWrapper(OpCodes.Ldind_I8, null)); } } }
private static bool MatchLdarg(OpCodeWrapper opc, out short arg) { if (opc.opcode == OpCodes.Ldarg) { arg = opc.ValueInt16; return true; } else if (opc.opcode == OpCodes.Ldarg_S) { arg = opc.ValueByte; return true; } else if (opc.opcode == OpCodes.Ldarg_0) { arg = 0; return true; } else if (opc.opcode == OpCodes.Ldarg_1) { arg = 1; return true; } else if (opc.opcode == OpCodes.Ldarg_2) { arg = 2; return true; } else if (opc.opcode == OpCodes.Ldarg_3) { arg = 3; return true; } else { arg = -1; return false; } }
private void ConvertSynchronizedFaultToFinally() { bool labelIndexSet = false; int start = -1; int nest = 0; int next = -1; for (int i = 0; i < code.Count; i++) { switch (code[i].pseudo) { case CodeType.BeginExceptionBlock: if (nest == 0) { start = i; } else if (nest == 1 && next <= start) { next = i; } nest++; break; case CodeType.BeginCatchBlock: case CodeType.BeginFinallyBlock: if (nest == 1) { nest = 0; if (next > start) { // while we were processing the outer block, we encountered a nested BeginExceptionBlock // so now that we've failed the outer, restart at the first nested block i = start = next; nest = 1; } } else { next = -1; } break; case CodeType.BeginFaultBlock: if (nest == 1) { int beginFault = i; if (code[i + 1].pseudo == CodeType.LineNumber) { i++; } // check if the fault handler is the synchronized block exit pattern if (code[i + 1].opcode == OpCodes.Ldloc && code[i + 2].pseudo == CodeType.MonitorExit && code[i + 3].opcode == OpCodes.Endfinally) { if (!labelIndexSet) { labelIndexSet = true; SetLabelIndexes(); } // now make two passes through the try block to 1) see if all leave // opcodes that leave the try block do a synchronized block exit // and 2) patch out the synchronized block exit for (int pass = 0; pass < 2; pass++) { for (int j = start; j < i; j++) { if (code[j].opcode == OpCodes.Leave) { int target = code[j].Label.Temp; if (target < start || target > i) { // check if the code preceding the leave matches the fault block if ((code[j - 1].opcode == OpCodes.Pop || code[j - 1].opcode == OpCodes.Stloc) && code[j - 2].pseudo == CodeType.MonitorExit && code[j - 3].Match(code[i + 1])) { if (pass == 1) { // move the leave to the top of the sequence we're removing code[j - 3] = code[j - 1]; code[j - 2] = code[j - 0]; code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); } } else if (code[j - 1].pseudo == CodeType.MonitorExit && code[j - 2].Match(code[i + 1])) { if (pass == 1) { // move the leave to the top of the sequence we're removing code[j - 2] = code[j]; code[j - 1] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); code[j - 0] = new OpCodeWrapper(CodeType.Unreachable, CodeTypeFlags.None); } } else { goto fail; } } } } } // if we end up here, all leaves have been successfully patched, // so now we turn the BeginFaultBlock into a BeginFinallyBlock code[beginFault] = new OpCodeWrapper(CodeType.BeginFinallyBlock, CodeTypeFlags.None); fail: ; } goto case CodeType.BeginFinallyBlock; } break; case CodeType.EndExceptionBlock: nest--; break; } } }
internal bool Match(OpCodeWrapper other) { return other.pseudo == pseudo && other.opcode == opcode && (other.data == data || (data != null && data.Equals(other.data))); }
private void MergeExceptionBlocks() { // The first loop will convert all Begin[Exception|Catch|Fault|Finally]Block and EndExceptionBlock // pseudo opcodes into a cyclic linked list (EndExceptionBlock links back to BeginExceptionBlock) // to allow for easy traversal in the next loop. int[] extra = new int[code.Count]; Stack<int> stack = new Stack<int>(); int currentBeginExceptionBlock = -1; int currentLast = -1; for (int i = 0; i < code.Count; i++) { switch (code[i].pseudo) { case CodeType.BeginExceptionBlock: stack.Push(currentBeginExceptionBlock); currentBeginExceptionBlock = i; currentLast = i; break; case CodeType.EndExceptionBlock: extra[currentLast] = i; extra[i] = currentBeginExceptionBlock; currentBeginExceptionBlock = stack.Pop(); currentLast = currentBeginExceptionBlock; if (currentLast != -1) { while (extra[currentLast] != 0) { currentLast = extra[currentLast]; } } break; case CodeType.BeginCatchBlock: case CodeType.BeginFaultBlock: case CodeType.BeginFinallyBlock: extra[currentLast] = i; currentLast = i; break; } } // Now we look for consecutive exception blocks that have the same fault handler for (int i = 0; i < code.Count - 1; i++) { if (code[i].pseudo == CodeType.EndExceptionBlock && code[i + 1].pseudo == CodeType.BeginExceptionBlock) { if (IsFaultOnlyBlock(extra, extra[i]) && IsFaultOnlyBlock(extra, i + 1)) { int beginFault1 = extra[extra[i]]; int beginFault2 = extra[i + 1]; int length1 = extra[beginFault1] - beginFault1; int length2 = extra[beginFault2] - beginFault2; if (length1 == length2 && MatchHandlers(beginFault1, beginFault2, length1)) { // Check if the labels at the start of the handler are reachable from outside // of the new combined block. for (int j = i + 2; j < beginFault2; j++) { if (code[j].pseudo == CodeType.OpCode) { break; } else if (code[j].pseudo == CodeType.Label) { if (HasBranchTo(0, extra[i], code[j].Label) || HasBranchTo(beginFault2 + length2, code.Count, code[j].Label)) { goto no_merge; } } } // Merge the two blocks by overwritting the first fault block and // the BeginExceptionBlock of the second block. for (int j = beginFault1; j < i + 2; j++) { code[j] = new OpCodeWrapper(OpCodes.Nop, null); } // Repair the linking structure. extra[extra[i]] = beginFault2; extra[extra[beginFault2]] = extra[i]; } } no_merge: ; } } }
private void DeduplicateBranchSourceTargetCode() { SetLabelIndexes(); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Br && code[i].HasLabel) { int source = i - 1; int target = code[i].Label.Temp - 1; while (source >= 0 && target >= 0) { switch (code[source].pseudo) { case CodeType.LineNumber: case CodeType.OpCode: break; default: goto break_while; } if (!code[source].Match(code[target])) { break; } switch (code[source].opcode.FlowControl) { case FlowControl.Branch: case FlowControl.Cond_Branch: goto break_while; } source--; target--; } break_while: ; source++; target++; if (source != i && target > 0 && source != target - 1) { // TODO for now we only do this optimization if there happens to be an appriopriate label if (code[target - 1].pseudo == CodeType.Label) { code[source] = new OpCodeWrapper(OpCodes.Br, code[target - 1].Label); for (int j = source + 1; j <= i; j++) { // We can't depend on DCE for code correctness (we have to maintain all MSIL invariants at all times), // so we patch out the unused code. code[j] = new OpCodeWrapper(CodeType.Unreachable, null); } } } } } }
private void ChaseBranches() { /* * Here we do a couple of different optimizations to unconditional branches: * - a branch to a ret or endfinally will be replaced * by the ret or endfinally instruction (because that is always at least as efficient) * - a branch to a branch will remove the indirection * - a leave to a branch or leave will remove the indirection */ SetLabelIndexes(); for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Br) { int target = code[i].Label.Temp + 1; if (code[target].pseudo == CodeType.LineNumber) { // line number info on endfinally or ret is probably useless anyway target++; } if (code[target].opcode == OpCodes.Endfinally || code[target].opcode == OpCodes.Ret) { code[i] = code[target]; } else { CodeEmitterLabel label = null; while (code[target].opcode == OpCodes.Br && target != i) { label = code[target].Label; target = code[target].Label.Temp + 1; } if (label != null) { code[i] = new OpCodeWrapper(OpCodes.Br, label); } } } else if (code[i].opcode == OpCodes.Leave) { int target = code[i].Label.Temp + 1; CodeEmitterLabel label = null; while ((code[target].opcode == OpCodes.Br || code[target].opcode == OpCodes.Leave) && target != i) { label = code[target].Label; target = code[target].Label.Temp + 1; } if (label != null) { code[i] = new OpCodeWrapper(OpCodes.Leave, label); } } } }
private void OptimizeLdcI8(int index) { long value = code[index].ValueInt64; OpCode opc = OpCodes.Nop; switch (value) { case -1: opc = OpCodes.Ldc_I4_M1; break; case 0: opc = OpCodes.Ldc_I4_0; break; case 1: opc = OpCodes.Ldc_I4_1; break; case 2: opc = OpCodes.Ldc_I4_2; break; case 3: opc = OpCodes.Ldc_I4_3; break; case 4: opc = OpCodes.Ldc_I4_4; break; case 5: opc = OpCodes.Ldc_I4_5; break; case 6: opc = OpCodes.Ldc_I4_6; break; case 7: opc = OpCodes.Ldc_I4_7; break; case 8: opc = OpCodes.Ldc_I4_8; break; default: if (value >= -2147483648L && value <= 4294967295L) { if (value >= -128 && value <= 127) { code[index] = new OpCodeWrapper(OpCodes.Ldc_I4_S, (sbyte)value); } else { code[index] = new OpCodeWrapper(OpCodes.Ldc_I4, (int)value); } if (value < 0) { code.Insert(index + 1, new OpCodeWrapper(OpCodes.Conv_I8, null)); } else { code.Insert(index + 1, new OpCodeWrapper(OpCodes.Conv_U8, null)); } } break; } if (opc != OpCodes.Nop) { code[index] = new OpCodeWrapper(opc, null); code.Insert(index + 1, new OpCodeWrapper(OpCodes.Conv_I8, null)); } }
private void PatchCompare(int index, OpCode ble, OpCode blt, OpCode bge, OpCode bgt) { if (code[index + 11].opcode == OpCodes.Brtrue) { code[index] = new OpCodeWrapper(OpCodes.Bne_Un, code[index + 11].Label); code.RemoveRange(index + 1, 11); } else if (code[index + 11].opcode == OpCodes.Brfalse) { code[index] = new OpCodeWrapper(OpCodes.Beq, code[index + 11].Label); code.RemoveRange(index + 1, 11); } else if (code[index + 11].opcode == OpCodes.Ldc_I4_0) { if (code[index + 12].opcode == OpCodes.Ble) { code[index] = new OpCodeWrapper(ble, code[index + 12].Label); code.RemoveRange(index + 1, 12); } else if (code[index + 12].opcode == OpCodes.Blt) { code[index] = new OpCodeWrapper(blt, code[index + 12].Label); code.RemoveRange(index + 1, 12); } else if (code[index + 12].opcode == OpCodes.Bge) { code[index] = new OpCodeWrapper(bge, code[index + 12].Label); code.RemoveRange(index + 1, 12); } else if (code[index + 12].opcode == OpCodes.Bgt) { code[index] = new OpCodeWrapper(bgt, code[index + 12].Label); code.RemoveRange(index + 1, 12); } } }
internal virtual void ReplaceOpCodeWrapper(OpCodeWrapper newOpCodes) { OpCodes.Dispose(); newOpCodes.Initialize(this); //implementing classes must carry out the substitution }