示例#1
0
        /// <summary>
        ///     Creates the new instructions, inlining each instantiation of each subroutine until the code is
        ///     fully elaborated.
        /// </summary>
        private void EmitCode()
        {
            var worklist = new LinkedList <Instantiation
                                           >();

            // Create an instantiation of the main "subroutine", which is just the main routine.
            worklist.AddLast(new Instantiation(this, null, mainSubroutineInsns)
                             );
            // Emit instantiations of each subroutine we encounter, including the main subroutine.
            var newInstructions   = new InsnList();
            var newTryCatchBlocks = new List <TryCatchBlockNode>();
            var newLocalVariables = new List <LocalVariableNode>();

            while (!(worklist.Count == 0))
            {
                var instantiation = Collections.RemoveFirst(worklist
                                                            );
                EmitInstantiation(instantiation, worklist, newInstructions, newTryCatchBlocks, newLocalVariables
                                  );
            }

            instructions   = newInstructions;
            tryCatchBlocks = newTryCatchBlocks;
            localVariables = newLocalVariables;
        }
示例#2
0
        /// <summary>Analyzes the given method.</summary>
        /// <param name="owner">the internal name of the class to which 'method' belongs.</param>
        /// <param name="method">the method to be analyzed.</param>
        /// <returns>
        ///     the symbolic state of the execution stack frame at each bytecode instruction of the
        ///     method. The size of the returned array is equal to the number of instructions (and labels)
        ///     of the method. A given frame is
        ///     <literal>null</literal>
        ///     if and only if the corresponding
        ///     instruction cannot be reached (dead code).
        /// </returns>
        /// <exception cref="AnalyzerException">if a problem occurs during the analysis.</exception>
        /// <exception cref="AnalyzerException" />
        public virtual Frame <V>[] Analyze(string owner, MethodNode method)
        {
            if ((method.access & (AccessFlags.Abstract | AccessFlags.Native
                                  )) != 0)
            {
                frames = new Frame <V> [0];
                return(frames);
            }

            insnList                 = method.instructions;
            insnListSize             = insnList.Size();
            handlers                 = (IList <TryCatchBlockNode>[]) new IList <object> [insnListSize];
            frames                   = new Frame <V> [insnListSize];
            subroutines              = new Subroutine[insnListSize];
            inInstructionsToProcess  = new bool[insnListSize];
            instructionsToProcess    = new int[insnListSize];
            numInstructionsToProcess = 0;
            // For each exception handler, and each instruction within its range, record in 'handlers' the
            // fact that execution can flow from this instruction to the exception handler.
            for (var i = 0; i < method.tryCatchBlocks.Count; ++i)
            {
                var tryCatchBlock = method.tryCatchBlocks[i];
                var startIndex    = insnList.IndexOf(tryCatchBlock.start);
                var endIndex      = insnList.IndexOf(tryCatchBlock.end);
                for (var j = startIndex; j < endIndex; ++j)
                {
                    var insnHandlers = handlers[j];
                    if (insnHandlers == null)
                    {
                        insnHandlers = new List <TryCatchBlockNode>();
                        handlers[j]  = insnHandlers;
                    }

                    insnHandlers.Add(tryCatchBlock);
                }
            }

            // For each instruction, compute the subroutine to which it belongs.
            // Follow the main 'subroutine', and collect the jsr instructions to nested subroutines.
            var main = new Subroutine(null, method.maxLocals, null);
            IList <AbstractInsnNode> jsrInsns = new List <AbstractInsnNode>();

            FindSubroutine(0, main, jsrInsns);
            // Follow the nested subroutines, and collect their own nested subroutines, until all
            // subroutines are found.
            IDictionary <LabelNode, Subroutine> jsrSubroutines = new Dictionary <LabelNode, Subroutine
                                                                                 >();

            while (!(jsrInsns.Count == 0))
            {
                var jsrInsn    = (JumpInsnNode)jsrInsns.RemoveAtReturningValue(0);
                var subroutine = jsrSubroutines.GetOrNull(jsrInsn.label);
                if (subroutine == null)
                {
                    subroutine = new Subroutine(jsrInsn.label, method.maxLocals, jsrInsn);
                    Collections.Put(jsrSubroutines, jsrInsn.label, subroutine);
                    FindSubroutine(insnList.IndexOf(jsrInsn.label), subroutine, jsrInsns);
                }
                else
                {
                    subroutine.callers.Add(jsrInsn);
                }
            }

            // Clear the main 'subroutine', which is not a real subroutine (and was used only as an
            // intermediate step above to find the real ones).
            for (var i = 0; i < insnListSize; ++i)
            {
                if (subroutines[i] != null && subroutines[i].start == null)
                {
                    subroutines[i] = null;
                }
            }
            // Initializes the data structures for the control flow analysis.
            var currentFrame = ComputeInitialFrame(owner, method);

            Merge(0, currentFrame, null);
            Init(owner, method);
            // Control flow analysis.
            while (numInstructionsToProcess > 0)
            {
                // Get and remove one instruction from the list of instructions to process.
                var insnIndex  = instructionsToProcess[--numInstructionsToProcess];
                var oldFrame   = frames[insnIndex];
                var subroutine = subroutines[insnIndex];
                inInstructionsToProcess[insnIndex] = false;
                // Simulate the execution of this instruction.
                AbstractInsnNode insnNode = null;
                try
                {
                    insnNode = method.instructions.Get(insnIndex);
                    var insnOpcode = insnNode.GetOpcode();
                    var insnType   = insnNode.GetType();
                    if (insnType == AbstractInsnNode.Label || insnType == AbstractInsnNode.Line || insnType
                        == AbstractInsnNode.Frame)
                    {
                        Merge(insnIndex + 1, oldFrame, subroutine);
                        NewControlFlowEdge(insnIndex, insnIndex + 1);
                    }
                    else
                    {
                        currentFrame.Init(oldFrame).Execute(insnNode, interpreter);
                        subroutine = subroutine == null ? null : new Subroutine(subroutine);
                        if (insnNode is JumpInsnNode)
                        {
                            var jumpInsn = (JumpInsnNode)insnNode;
                            if (insnOpcode != OpcodesConstants.Goto && insnOpcode != OpcodesConstants.Jsr)
                            {
                                currentFrame.InitJumpTarget(insnOpcode, null);
                                /* target = */
                                Merge(insnIndex + 1, currentFrame, subroutine);
                                NewControlFlowEdge(insnIndex, insnIndex + 1);
                            }

                            var jumpInsnIndex = insnList.IndexOf(jumpInsn.label);
                            currentFrame.InitJumpTarget(insnOpcode, jumpInsn.label);
                            if (insnOpcode == OpcodesConstants.Jsr)
                            {
                                Merge(jumpInsnIndex, currentFrame, new Subroutine(jumpInsn.label, method.maxLocals
                                                                                  , jumpInsn));
                            }
                            else
                            {
                                Merge(jumpInsnIndex, currentFrame, subroutine);
                            }
                            NewControlFlowEdge(insnIndex, jumpInsnIndex);
                        }
                        else if (insnNode is LookupSwitchInsnNode)
                        {
                            var lookupSwitchInsn = (LookupSwitchInsnNode)insnNode;
                            var targetInsnIndex  = insnList.IndexOf(lookupSwitchInsn.dflt);
                            currentFrame.InitJumpTarget(insnOpcode, lookupSwitchInsn.dflt);
                            Merge(targetInsnIndex, currentFrame, subroutine);
                            NewControlFlowEdge(insnIndex, targetInsnIndex);
                            for (var i = 0; i < lookupSwitchInsn.labels.Count; ++i)
                            {
                                var label = lookupSwitchInsn.labels[i];
                                targetInsnIndex = insnList.IndexOf(label);
                                currentFrame.InitJumpTarget(insnOpcode, label);
                                Merge(targetInsnIndex, currentFrame, subroutine);
                                NewControlFlowEdge(insnIndex, targetInsnIndex);
                            }
                        }
                        else if (insnNode is TableSwitchInsnNode)
                        {
                            var tableSwitchInsn = (TableSwitchInsnNode)insnNode;
                            var targetInsnIndex = insnList.IndexOf(tableSwitchInsn.dflt);
                            currentFrame.InitJumpTarget(insnOpcode, tableSwitchInsn.dflt);
                            Merge(targetInsnIndex, currentFrame, subroutine);
                            NewControlFlowEdge(insnIndex, targetInsnIndex);
                            for (var i = 0; i < tableSwitchInsn.labels.Count; ++i)
                            {
                                var label = tableSwitchInsn.labels[i];
                                currentFrame.InitJumpTarget(insnOpcode, label);
                                targetInsnIndex = insnList.IndexOf(label);
                                Merge(targetInsnIndex, currentFrame, subroutine);
                                NewControlFlowEdge(insnIndex, targetInsnIndex);
                            }
                        }
                        else if (insnOpcode == OpcodesConstants.Ret)
                        {
                            if (subroutine == null)
                            {
                                throw new AnalyzerException(insnNode, "RET instruction outside of a subroutine");
                            }
                            for (var i = 0; i < subroutine.callers.Count; ++i)
                            {
                                var caller       = subroutine.callers[i];
                                var jsrInsnIndex = insnList.IndexOf(caller);
                                if (frames[jsrInsnIndex] != null)
                                {
                                    Merge(jsrInsnIndex + 1, frames[jsrInsnIndex], currentFrame, subroutines[jsrInsnIndex
                                          ], subroutine.localsUsed);
                                    NewControlFlowEdge(insnIndex, jsrInsnIndex + 1);
                                }
                            }
                        }
                        else if (insnOpcode != OpcodesConstants.Athrow &&
                                 (insnOpcode < OpcodesConstants.Ireturn || insnOpcode > OpcodesConstants.Return))
                        {
                            if (subroutine != null)
                            {
                                if (insnNode is VarInsnNode)
                                {
                                    var var = ((VarInsnNode)insnNode).var;
                                    subroutine.localsUsed[var] = true;
                                    if (insnOpcode == OpcodesConstants.Lload || insnOpcode == OpcodesConstants.Dload ||
                                        insnOpcode == OpcodesConstants.Lstore ||
                                        insnOpcode == OpcodesConstants.Dstore)
                                    {
                                        subroutine.localsUsed[var + 1] = true;
                                    }
                                }
                                else if (insnNode is IincInsnNode)
                                {
                                    var var = ((IincInsnNode)insnNode).var;
                                    subroutine.localsUsed[var] = true;
                                }
                            }

                            Merge(insnIndex + 1, currentFrame, subroutine);
                            NewControlFlowEdge(insnIndex, insnIndex + 1);
                        }
                    }

                    var insnHandlers = handlers[insnIndex];
                    if (insnHandlers != null)
                    {
                        foreach (var tryCatchBlock in insnHandlers)
                        {
                            Type catchType;
                            if (tryCatchBlock.type == null)
                            {
                                catchType = Type.GetObjectType("java/lang/Throwable");
                            }
                            else
                            {
                                catchType = Type.GetObjectType(tryCatchBlock.type);
                            }
                            if (NewControlFlowExceptionEdge(insnIndex, tryCatchBlock))
                            {
                                var handler = NewFrame(oldFrame);
                                handler.ClearStack();
                                handler.Push(interpreter.NewExceptionValue(tryCatchBlock, handler, catchType));
                                Merge(insnList.IndexOf(tryCatchBlock.handler), handler, subroutine);
                            }
                        }
                    }
                }
                catch (AnalyzerException e)
                {
                    throw new AnalyzerException(e.node, "Error at instruction " + insnIndex + ": " +
                                                e.Message, e);
                }
                catch (Exception e)
                {
                    // DontCheck(IllegalCatch): can't be fixed, for backward compatibility.
                    throw new AnalyzerException(insnNode, "Error at instruction " + insnIndex + ": "
                                                + e.Message, e);
                }
            }

            return(frames);
        }
示例#3
0
        /// <summary>
        ///     Emits an instantiation of a subroutine, specified by <code>instantiation</code>.
        /// </summary>
        /// <remarks>
        ///     Emits an instantiation of a subroutine, specified by <code>instantiation</code>. May add new
        ///     instantiations that are invoked by this one to the <code>worklist</code>, and new try/catch
        ///     blocks to <code>newTryCatchBlocks</code>.
        /// </remarks>
        /// <param name="instantiation">the instantiation that must be performed.</param>
        /// <param name="worklist">list of the instantiations that remain to be done.</param>
        /// <param name="newInstructions">
        ///     the instruction list to which the instantiated code must be appended.
        /// </param>
        /// <param name="newTryCatchBlocks">
        ///     the exception handler list to which the instantiated handlers must be
        ///     appended.
        /// </param>
        /// <param name="newLocalVariables">
        ///     the local variables list to which the instantiated local variables
        ///     must be appended.
        /// </param>
        private void EmitInstantiation(Instantiation instantiation, LinkedList
                                       <Instantiation> worklist, InsnList newInstructions, IList <TryCatchBlockNode
                                                                                                  > newTryCatchBlocks, IList <LocalVariableNode> newLocalVariables)
        {
            LabelNode previousLabelNode = null;

            for (var i = 0; i < instructions.Size(); ++i)
            {
                var insnNode = instructions.Get(i);
                if (insnNode.GetType() == AbstractInsnNode.Label)
                {
                    // Always clone all labels, while avoiding to add the same label more than once.
                    var labelNode       = (LabelNode)insnNode;
                    var clonedLabelNode = instantiation.GetClonedLabel(labelNode);
                    if (clonedLabelNode != previousLabelNode)
                    {
                        newInstructions.Add(clonedLabelNode);
                        previousLabelNode = clonedLabelNode;
                    }
                }
                else if (instantiation.FindOwner(i) == instantiation)
                {
                    // Don't emit instructions that were already emitted by an ancestor subroutine. Note that it
                    // is still possible for a given instruction to be emitted twice because it may belong to
                    // two subroutines that do not invoke each other.
                    if (insnNode.GetOpcode() == OpcodesConstants.Ret)
                    {
                        // Translate RET instruction(s) to a jump to the return label for the appropriate
                        // instantiation. The problem is that the subroutine may "fall through" to the ret of a
                        // parent subroutine; therefore, to find the appropriate ret label we find the oldest
                        // instantiation that claims to own this instruction.
                        LabelNode retLabel = null;
                        for (var retLabelOwner = instantiation;
                             retLabelOwner
                             != null;
                             retLabelOwner = retLabelOwner.parent)
                        {
                            if (retLabelOwner.subroutineInsns.Get(i))
                            {
                                retLabel = retLabelOwner.returnLabel;
                            }
                        }
                        if (retLabel == null)
                        {
                            // This is only possible if the mainSubroutine owns a RET instruction, which should
                            // never happen for verifiable code.
                            throw new ArgumentException("Instruction #" + i + " is a RET not owned by any subroutine"
                                                        );
                        }
                        newInstructions.Add(new JumpInsnNode(OpcodesConstants.Goto, retLabel));
                    }
                    else if (insnNode.GetOpcode() == OpcodesConstants.Jsr)
                    {
                        var jsrLabelNode     = ((JumpInsnNode)insnNode).label;
                        var subroutineInsns  = subroutinesInsns.GetOrNull(jsrLabelNode);
                        var newInstantiation = new Instantiation
                                                   (this, instantiation, subroutineInsns);
                        var clonedJsrLabelNode = newInstantiation.GetClonedLabelForJumpInsn(jsrLabelNode
                                                                                            );
                        // Replace the JSR instruction with a GOTO to the instantiated subroutine, and push NULL
                        // for what was once the return address value. This hack allows us to avoid doing any sort
                        // of data flow analysis to figure out which instructions manipulate the old return
                        // address value pointer which is now known to be unneeded.
                        newInstructions.Add(new InsnNode(OpcodesConstants.Aconst_Null));
                        newInstructions.Add(new JumpInsnNode(OpcodesConstants.Goto, clonedJsrLabelNode));
                        newInstructions.Add(newInstantiation.returnLabel);
                        // Insert this new instantiation into the queue to be emitted later.
                        worklist.AddLast(newInstantiation);
                    }
                    else
                    {
                        newInstructions.Add(insnNode.Clone(instantiation));
                    }
                }
            }

            // Emit the try/catch blocks that are relevant for this instantiation.
            foreach (var tryCatchBlockNode in tryCatchBlocks)
            {
                var start = instantiation.GetClonedLabel(tryCatchBlockNode.start);
                var end   = instantiation.GetClonedLabel(tryCatchBlockNode.end);
                if (start != end)
                {
                    var handler = instantiation.GetClonedLabelForJumpInsn(tryCatchBlockNode.handler
                                                                          );
                    if (start == null || end == null || handler == null)
                    {
                        throw new AssertionError("Internal error!");
                    }
                    newTryCatchBlocks.Add(new TryCatchBlockNode(start, end, handler, tryCatchBlockNode
                                                                .type));
                }
            }

            // Emit the local variable nodes that are relevant for this instantiation.
            foreach (var localVariableNode in localVariables)
            {
                var start = instantiation.GetClonedLabel(localVariableNode.start);
                var end   = instantiation.GetClonedLabel(localVariableNode.end);
                if (start != end)
                {
                    newLocalVariables.Add(new LocalVariableNode(localVariableNode.name, localVariableNode
                                                                .desc, localVariableNode.signature, start, end, localVariableNode.index));
                }
            }
        }