public override void WriteTo(FlashWriter output) { var marks = new Dictionary <ASInstruction, long>(); var sharedExits = new Dictionary <ASInstruction, List <ASInstruction> >(); var forwardCaseExits = new Dictionary <ASInstruction, Tuple <LookUpSwitchIns, int> >(); foreach (ASInstruction instruction in _instructions) { long previousPosition = output.Position; marks.Add(instruction, previousPosition); instruction.WriteTo(output); List <ASInstruction> jumpers = null; if (sharedExits.TryGetValue(instruction, out jumpers)) { foreach (ASInstruction jumper in jumpers) { long position = marks[jumper]; var fixedOffset = (uint)(previousPosition - (position + 4)); ((Jumper)jumper).Offset = fixedOffset; Rewrite(output, jumper, position); } sharedExits.Remove(instruction); } Tuple <LookUpSwitchIns, int> switchIdentity = null; if (forwardCaseExits.TryGetValue(instruction, out switchIdentity)) { int caseIndex = switchIdentity.Item2; LookUpSwitchIns lookUpSwitch = switchIdentity.Item1; long position = marks[lookUpSwitch]; var fixedOffset = (uint)(previousPosition - position); if (caseIndex == lookUpSwitch.CaseOffsets.Count) { lookUpSwitch.DefaultOffset = fixedOffset; } else { lookUpSwitch.CaseOffsets[caseIndex] = fixedOffset; } Rewrite(output, lookUpSwitch, position); } if (instruction.OP == OPCode.LookUpSwitch) { bool requiresRewrite = false; var lookUpSwitch = (LookUpSwitchIns)instruction; ASInstruction[] cases = SwitchExits[lookUpSwitch]; for (int i = 0; i < cases.Length; i++) { ASInstruction exit = cases[i]; if (exit.OP != OPCode.Label) { forwardCaseExits.Add(exit, Tuple.Create(lookUpSwitch, i)); } else { requiresRewrite = true; long exitPosition = marks[exit]; long jumpCount = (previousPosition - (exitPosition + 1)); uint fixedOffset = (uint)(uint.MaxValue - jumpCount); if (i == (cases.Length - 1)) { lookUpSwitch.DefaultOffset = fixedOffset; } else { lookUpSwitch.CaseOffsets[i] = fixedOffset; } } } if (requiresRewrite) { Rewrite(output, lookUpSwitch, previousPosition); } } else if (Jumper.IsValid(instruction.OP)) { var jumper = (Jumper)instruction; if (jumper.Offset == 0) { continue; } ASInstruction exit = null; if (!JumpExits.TryGetValue(jumper, out exit)) { // An exit for this jump instruction could not be found, perhaps its' offset exceeds the index limit? continue; } else if (exit.OP != OPCode.Label || !marks.ContainsKey(exit)) // Forward jumps can have a label for an exit, as long as it is located ahead. { jumpers = null; if (!sharedExits.TryGetValue(exit, out jumpers)) { jumpers = new List <ASInstruction>(); sharedExits.Add(exit, jumpers); } jumpers.Add(jumper); } else // Backward jumps must always have a label for an exit, and must be behind this instruction. { long exitPosition = marks[exit]; long jumpCount = (output.Position - (exitPosition + 1)); var fixedOffset = (uint)(uint.MaxValue - jumpCount); jumper.Offset = fixedOffset; Rewrite(output, jumper, previousPosition); } } } }
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); }