public void RemoveRange(int index, int count) { if ((index + count) <= _instructions.Count) { for (int i = 0; i < count; i++) { ASInstruction instruction = _instructions[index]; _indices.Remove(instruction); _instructions.RemoveAt(index); List <ASInstruction> group = _opGroups[instruction.OP]; if (group.Count == 1) { _opGroups.Remove(instruction.OP); } else { group.Remove(instruction); } // TODO: Recalibrate switch exits. Jumper entry = GetJumperEntry(instruction); if (entry != null) { if (index != _instructions.Count) { ASInstruction exit = _instructions[index]; JumpExits[entry] = exit; } else { JumpExits[entry] = null; } } if (Jumper.IsValid(instruction.OP)) { JumpExits.Remove((Jumper)instruction); } else if (instruction.OP == OPCode.LookUpSwitch) { SwitchExits.Remove((LookUpSwitchIns)instruction); } } for (int i = index; i < _indices.Count; i++) { ASInstruction toPull = _instructions[i]; _indices[toPull] -= count; } } }
private int GetFinalJumpCount(ASMachine machine, Jumper jumper, List <ASInstruction> cleaned, Dictionary <int, List <ASInstruction> > localReferences, Stack <ASInstruction> valuePushers) { var magicCount = 0; var locals = new List <Local>(); var pushers = new List <ASInstruction>(); bool?isJumping = jumper.RunCondition(machine); // Get the instructions that pushed the values the jump instruction used. int popCount = jumper.GetPopCount(); for (int i = 0; i < popCount; i++) { ASInstruction pusher = valuePushers.Pop(); if (!pushers.Contains(pusher)) { pushers.Add(pusher); // Get the instructions that were pushed by a GetLocal/N. // These are used to determine whether the jump should be kept, since a local register could change within the jump body. if (Local.IsValid(pusher.OP)) { locals.Add((Local)pusher); } else if (Primitive.IsValid(pusher.OP) || pusher.OP == OPCode.Dup) { magicCount++; } } } // Output is not known, keep the instruction. if (isJumping == null) { cleaned.Add(jumper); return(0); } if (pushers.Count != (magicCount + locals.Count)) { // One or more push instructions are wildcards, they have a 'history' of being modified. // Keep this jump instruction, result could change. cleaned.Add(jumper); return(0); } // Gather information about the jump instruction, and it's 'block' of instructions that are being jumped over(if 'isJumping = true'). ASInstruction exit = null; bool isBackwardsJump = false; IEnumerable <ASInstruction> block = null; if (!JumpExits.TryGetValue(jumper, out exit)) { // This jump instruction should not be 'cleaned', keep it. cleaned.Add(jumper); return(0); } if (IsBackwardsJump(jumper)) { isBackwardsJump = true; block = cleaned .Skip(cleaned.IndexOf(exit) + 1) .TakeWhile(i => i != jumper); } else { block = (jumper.Offset > 0 ? GetJumpBlock(jumper) : null); } if (isJumping == true && block != null) { if (isBackwardsJump) { // Check if any of the locals used by the jump instruction is being set within the body. // If the anwser is yes, removing the jump instruction is a bad idea, since the output of the condition could change. foreach (Local local in locals) { foreach (ASInstruction instruction in block) { if (!Local.IsValid(instruction.OP)) { continue; } if (Local.IsGetLocal(instruction.OP)) { continue; } var bodyLocal = (Local)instruction; if (bodyLocal.Register == local.Register) { // Do not remove the jump instruction, condition result may change. cleaned.Add(jumper); return(0); } } } } foreach (KeyValuePair <Jumper, ASInstruction> jumpExit in JumpExits) { if (jumpExit.Key == jumper) { continue; } bool hasEntry = block.Contains(jumpExit.Key); // Does a jump instruction begin somewhere in the block? bool hasExit = block.Contains(jumpExit.Value); // Does the jump instruction end somewhere in the block? ASInstruction afterLast = _instructions[_indices[block.Last()] + 1]; bool isExitAfterLast = (jumpExit.Value == afterLast); // Does the exit of the jump that is in the block come after the final instruction of the current block? if (hasEntry && !hasExit && !isExitAfterLast || hasExit && !hasEntry) { // Keep the jump instruction, since it will corrupt the other jump instruction that is using it. cleaned.Add(jumper); return(0); } } foreach (KeyValuePair <LookUpSwitchIns, ASInstruction[]> switchExit in SwitchExits) { foreach (ASInstruction caseExit in switchExit.Value) { if (block.Contains(caseExit)) { cleaned.Add(jumper); return(0); } } } } foreach (Local local in locals) { List <ASInstruction> references = localReferences[local.Register]; references.Remove(local); } // Remove the instructions that pushed values for the jump instruction that is to be removed. foreach (ASInstruction pusher in pushers) { cleaned.Remove(pusher); } if (isJumping == false || isBackwardsJump) { block = null; } JumpExits.Remove(jumper); return(block?.Count() ?? 0); }