예제 #1
0
        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);
                    }
                }
            }
        }
예제 #2
0
        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);
        }