View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.bcel.verifier.structurals;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.bcel.generic.ATHROW;
26  import org.apache.bcel.generic.BranchInstruction;
27  import org.apache.bcel.generic.GotoInstruction;
28  import org.apache.bcel.generic.Instruction;
29  import org.apache.bcel.generic.InstructionHandle;
30  import org.apache.bcel.generic.JsrInstruction;
31  import org.apache.bcel.generic.MethodGen;
32  import org.apache.bcel.generic.RET;
33  import org.apache.bcel.generic.ReturnInstruction;
34  import org.apache.bcel.generic.Select;
35  import org.apache.bcel.verifier.exc.AssertionViolatedException;
36  import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
37  
38  /**
39   * This class represents a control flow graph of a method.
40   */
41  public class ControlFlowGraph {
42  
43      /**
44       * Objects of this class represent a node in a ControlFlowGraph. These nodes are instructions, not basic blocks.
45       */
46      private final class InstructionContextImpl implements InstructionContext {
47  
48          /**
49           * The TAG field is here for external temporary flagging, such as graph coloring.
50           *
51           * @see #getTag()
52           * @see #setTag(int)
53           */
54          private int TAG;
55  
56          /**
57           * The InstructionHandle this InstructionContext is wrapped around.
58           */
59          private final InstructionHandle instruction;
60  
61          /**
62           * The 'incoming' execution Frames.
63           */
64          private final Map<InstructionContext, Frame> inFrames; // key: the last-executed JSR
65  
66          /**
67           * The 'outgoing' execution Frames.
68           */
69          private final Map<InstructionContext, Frame> outFrames; // key: the last-executed JSR
70  
71          /**
72           * The 'execution predecessors' - a list of type InstructionContext of those instances that have been execute()d before
73           * in that order.
74           */
75          private List<InstructionContext> executionPredecessors; // Type: InstructionContext
76  
77          /**
78           * Creates an InstructionHandleImpl object from an InstructionHandle. Creation of one per InstructionHandle suffices.
79           * Don't create more.
80           */
81          public InstructionContextImpl(final InstructionHandle inst) {
82              if (inst == null) {
83                  throw new AssertionViolatedException("Cannot instantiate InstructionContextImpl from NULL.");
84              }
85  
86              instruction = inst;
87              inFrames = new HashMap<>();
88              outFrames = new HashMap<>();
89          }
90  
91          /**
92           * A utility method that calculates the successors of a given InstructionHandle That means, a RET does have successors
93           * as defined here. A JsrInstruction has its target as its successor (opposed to its physical successor) as defined
94           * here.
95           */
96  // TODO: implement caching!
97          private InstructionHandle[] _getSuccessors() {
98              final InstructionHandle[] single = new InstructionHandle[1];
99  
100             final Instruction inst = getInstruction().getInstruction();
101 
102             if (inst instanceof RET) {
103                 final Subroutine s = subroutines.subroutineOf(getInstruction());
104                 if (s == null) { // return empty;
105                     // RET in dead code. "empty" would be the correct answer, but we know something about the surrounding project...
106                     throw new AssertionViolatedException("Asking for successors of a RET in dead code?!");
107                 }
108 
109 //TODO: remove. Only JustIce must not use it, but foreign users of the ControlFlowGraph
110 //      will want it. Thanks Johannes Wust.
111 //throw new AssertionViolatedException("DID YOU REALLY WANT TO ASK FOR RET'S SUCCS?");
112 
113                 final InstructionHandle[] jsrs = s.getEnteringJsrInstructions();
114                 final InstructionHandle[] ret = new InstructionHandle[jsrs.length];
115                 Arrays.setAll(ret, i -> jsrs[i].getNext());
116                 return ret;
117             }
118 
119             // Terminates method normally.
120             // Terminates method abnormally, because JustIce mandates
121             // subroutines not to be protected by exception handlers.
122             if (inst instanceof ReturnInstruction || inst instanceof ATHROW) {
123                 return InstructionHandle.EMPTY_ARRAY;
124             }
125 
126             // See method comment.
127             if (inst instanceof JsrInstruction) {
128                 single[0] = ((JsrInstruction) inst).getTarget();
129                 return single;
130             }
131 
132             if (inst instanceof GotoInstruction) {
133                 single[0] = ((GotoInstruction) inst).getTarget();
134                 return single;
135             }
136 
137             if (inst instanceof BranchInstruction) {
138                 if (inst instanceof Select) {
139                     // BCEL's getTargets() returns only the non-default targets,
140                     // thanks to Eli Tilevich for reporting.
141                     final InstructionHandle[] matchTargets = ((Select) inst).getTargets();
142                     final InstructionHandle[] ret = new InstructionHandle[matchTargets.length + 1];
143                     ret[0] = ((Select) inst).getTarget();
144                     System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
145                     return ret;
146                 }
147                 final InstructionHandle[] pair = new InstructionHandle[2];
148                 pair[0] = getInstruction().getNext();
149                 pair[1] = ((BranchInstruction) inst).getTarget();
150                 return pair;
151             }
152 
153             // default case: Fall through.
154             single[0] = getInstruction().getNext();
155             return single;
156         }
157 
158         /**
159          * "Merges in" (vmspec2, page 146) the "incoming" frame situation; executes the instructions symbolically and therefore
160          * calculates the "outgoing" frame situation. Returns: True iff the "incoming" frame situation changed after merging
161          * with "inFrame". The execPreds ArrayList must contain the InstructionContext objects executed so far in the correct
162          * order. This is just one execution path [out of many]. This is needed to correctly "merge" in the special case of a
163          * RET's successor. <B>The InstConstraintVisitor and ExecutionVisitor instances must be set up correctly.</B>
164          *
165          * @return true - if and only if the "outgoing" frame situation changed from the one before execute()ing.
166          */
167         @Override
168         public boolean execute(final Frame inFrame, final ArrayList<InstructionContext> execPreds, final InstConstraintVisitor icv, final ExecutionVisitor ev) {
169 
170             @SuppressWarnings("unchecked") // OK because execPreds is compatible type
171             final List<InstructionContext> clone = (List<InstructionContext>) execPreds.clone();
172             executionPredecessors = clone;
173 
174             // sanity check
175             if (lastExecutionJSR() == null && subroutines.subroutineOf(getInstruction()) != subroutines.getTopLevel()
176                 || lastExecutionJSR() != null && subroutines.subroutineOf(getInstruction()) == subroutines.getTopLevel()) {
177                 throw new AssertionViolatedException("Huh?! Am I '" + this + "' part of a subroutine or not?");
178             }
179 
180             Frame inF = inFrames.get(lastExecutionJSR());
181             if (inF == null) { // no incoming frame was set, so set it.
182                 inFrames.put(lastExecutionJSR(), inFrame);
183                 inF = inFrame;
184             } else if (inF.equals(inFrame) || !mergeInFrames(inFrame)) { // if there was an "old" inFrame
185                 return false;
186             }
187 
188             // Now we're sure the inFrame has changed!
189 
190             // new inFrame is already merged in, see above.
191             final Frame workingFrame = inF.getClone();
192 
193             try {
194                 // This verifies the InstructionConstraint for the current
195                 // instruction, but does not modify the workingFrame object.
196 //InstConstraintVisitor icv = InstConstraintVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName()));
197                 icv.setFrame(workingFrame);
198                 getInstruction().accept(icv);
199             } catch (final StructuralCodeConstraintException ce) {
200                 ce.extendMessage("", "\nInstructionHandle: " + getInstruction() + "\n");
201                 ce.extendMessage("", "\nExecution Frame:\n" + workingFrame);
202                 extendMessageWithFlow(ce);
203                 throw ce;
204             }
205 
206             // This executes the Instruction.
207             // Therefore the workingFrame object is modified.
208 //ExecutionVisitor ev = ExecutionVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName()));
209             ev.setFrame(workingFrame);
210             getInstruction().accept(ev);
211             // getInstruction().accept(ExecutionVisitor.withFrame(workingFrame));
212             outFrames.put(lastExecutionJSR(), workingFrame);
213 
214             return true; // new inFrame was different from old inFrame so merging them
215                          // yielded a different this.inFrame.
216         }
217 
218         /**
219          * Extends the StructuralCodeConstraintException ("e") object with an at-the-end-extended message. This extended message
220          * will then reflect the execution flow needed to get to the constraint violation that triggered the throwing of the "e"
221          * object.
222          */
223         private void extendMessageWithFlow(final StructuralCodeConstraintException e) {
224             final String s = "Execution flow:\n";
225             e.extendMessage("", s + getExecutionChain());
226         }
227 
228         /**
229          * Returns the exception handlers of this instruction.
230          */
231         @Override
232         public ExceptionHandler[] getExceptionHandlers() {
233             return exceptionhandlers.getExceptionHandlers(getInstruction());
234         }
235 
236         /**
237          * Returns the control flow execution chain. This is built while execute(Frame, ArrayList)-ing the code represented by
238          * the surrounding ControlFlowGraph.
239          */
240         private String getExecutionChain() {
241             final StringBuilder s = new StringBuilder(toString());
242             for (int i = executionPredecessors.size() - 1; i >= 0; i--) {
243                 s.insert(0, executionPredecessors.get(i) + "\n");
244             }
245             return s.toString();
246         }
247 
248         @Override
249         public Frame getInFrame() {
250             Frame org;
251 
252             final InstructionContext jsr = lastExecutionJSR();
253 
254             org = inFrames.get(jsr);
255 
256             if (org == null) {
257                 throw new AssertionViolatedException("inFrame not set! This:\n" + this + "\nInFrames: '" + inFrames + "'.");
258             }
259             return org.getClone();
260         }
261 
262         @Override
263         public InstructionHandle getInstruction() {
264             return instruction;
265         }
266 
267         /**
268          * Returns a clone of the "outgoing" frame situation with respect to the given ExecutionChain.
269          */
270         @Override
271         public Frame getOutFrame(final ArrayList<InstructionContext> execChain) {
272             executionPredecessors = execChain;
273 
274             Frame org;
275 
276             final InstructionContext jsr = lastExecutionJSR();
277 
278             org = outFrames.get(jsr);
279 
280             if (org == null) {
281                 throw new AssertionViolatedException(
282                     "outFrame not set! This:\n" + this + "\nExecutionChain: " + getExecutionChain() + "\nOutFrames: '" + outFrames + "'.");
283             }
284             return org.getClone();
285         }
286 
287         @Override
288         public InstructionContext[] getSuccessors() {
289             return contextsOf(_getSuccessors());
290         }
291 
292         @Override
293         public int getTag() {
294             return TAG;
295         }
296 
297         /**
298          * Returns the InstructionContextImpl with an JSR/JSR_W that was last in the ExecutionChain, without a corresponding
299          * RET, i.e. we were called by this one. Returns null if we were called from the top level.
300          */
301         private InstructionContextImpl lastExecutionJSR() {
302 
303             final int size = executionPredecessors.size();
304             int retcount = 0;
305 
306             for (int i = size - 1; i >= 0; i--) {
307                 final InstructionContextImpl current = (InstructionContextImpl) executionPredecessors.get(i);
308                 final Instruction currentlast = current.getInstruction().getInstruction();
309                 if (currentlast instanceof RET) {
310                     retcount++;
311                 }
312                 if (currentlast instanceof JsrInstruction) {
313                     retcount--;
314                     if (retcount == -1) {
315                         return current;
316                     }
317                 }
318             }
319             return null;
320         }
321 
322         /**
323          * Does the actual merging (vmspec2, page 146). Returns true IFF this.inFrame was changed in course of merging with
324          * inFrame.
325          */
326         private boolean mergeInFrames(final Frame inFrame) {
327             // TODO: Can be performance-improved.
328             final Frame inF = inFrames.get(lastExecutionJSR());
329             final OperandStack oldstack = inF.getStack().getClone();
330             final LocalVariables oldlocals = inF.getLocals().getClone();
331             try {
332                 inF.getStack().merge(inFrame.getStack());
333                 inF.getLocals().merge(inFrame.getLocals());
334             } catch (final StructuralCodeConstraintException sce) {
335                 extendMessageWithFlow(sce);
336                 throw sce;
337             }
338             return !(oldstack.equals(inF.getStack()) && oldlocals.equals(inF.getLocals()));
339         }
340 
341         /* Satisfies InstructionContext.setTag(int). */
342         @Override
343         public void setTag(final int tag) { // part of InstructionContext interface
344             TAG = tag;
345         }
346 
347         /**
348          * Returns a simple String representation of this InstructionContext.
349          */
350         @Override
351         public String toString() {
352             // TODO: Put information in the brackets, e.g.
353             // Is this an ExceptionHandler? Is this a RET? Is this the start of
354             // a subroutine?
355             return getInstruction().toString(false) + "\t[InstructionContext]";
356         }
357 
358     } // End Inner InstructionContextImpl Class.
359 
360     /// ** The MethodGen object we're working on. */
361     // private final MethodGen method_gen;
362 
363     /** The Subroutines object for the method whose control flow is represented by this ControlFlowGraph. */
364     private final Subroutines subroutines;
365 
366     /** The ExceptionHandlers object for the method whose control flow is represented by this ControlFlowGraph. */
367     private final ExceptionHandlers exceptionhandlers;
368 
369     /** All InstructionContext instances of this ControlFlowGraph. */
370     private final Map<InstructionHandle, InstructionContext> instructionContexts = new HashMap<>();
371 
372     /**
373      * A Control Flow Graph; with additional JustIce checks
374      *
375      * @param methodGen the method generator instance
376      */
377     public ControlFlowGraph(final MethodGen methodGen) {
378         this(methodGen, true);
379     }
380 
381     /**
382      * A Control Flow Graph.
383      *
384      * @param methodGen the method generator instance
385      * @param enableJustIceCheck if true, additional JustIce checks are performed
386      * @since 6.0
387      */
388     public ControlFlowGraph(final MethodGen methodGen, final boolean enableJustIceCheck) {
389         subroutines = new Subroutines(methodGen, enableJustIceCheck);
390         exceptionhandlers = new ExceptionHandlers(methodGen);
391 
392         final InstructionHandle[] instructionhandles = methodGen.getInstructionList().getInstructionHandles();
393         for (final InstructionHandle instructionhandle : instructionhandles) {
394             instructionContexts.put(instructionhandle, new InstructionContextImpl(instructionhandle));
395         }
396 
397         // this.method_gen = method_gen;
398     }
399 
400     /**
401      * Returns the InstructionContext of a given instruction.
402      */
403     public InstructionContext contextOf(final InstructionHandle inst) {
404         final InstructionContext ic = instructionContexts.get(inst);
405         if (ic == null) {
406             throw new AssertionViolatedException("InstructionContext requested for an InstructionHandle that's not known!");
407         }
408         return ic;
409     }
410 
411     /**
412      * Returns the InstructionContext[] of a given InstructionHandle[], in a naturally ordered manner.
413      */
414     public InstructionContext[] contextsOf(final InstructionHandle[] insts) {
415         final InstructionContext[] ret = new InstructionContext[insts.length];
416         Arrays.setAll(ret, i -> contextOf(insts[i]));
417         return ret;
418     }
419 
420     /**
421      * Returns an InstructionContext[] with all the InstructionContext instances for the method whose control flow is
422      * represented by this ControlFlowGraph <B>(NOT ORDERED!)</B>.
423      */
424     public InstructionContext[] getInstructionContexts() {
425         final InstructionContext[] ret = new InstructionContext[instructionContexts.size()];
426         return instructionContexts.values().toArray(ret);
427     }
428 
429     /**
430      * Returns true, if and only if the said instruction is not reachable; that means, if it is not part of this
431      * ControlFlowGraph.
432      */
433     public boolean isDead(final InstructionHandle i) {
434         return subroutines.subroutineOf(i) == null;
435     }
436 }