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.commons.javaflow.bytecode.transformation.bcel.analyser;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.Hashtable;
22  
23  import org.apache.bcel.generic.ATHROW;
24  import org.apache.bcel.generic.BranchInstruction;
25  import org.apache.bcel.generic.GotoInstruction;
26  import org.apache.bcel.generic.Instruction;
27  import org.apache.bcel.generic.InstructionHandle;
28  import org.apache.bcel.generic.JsrInstruction;
29  import org.apache.bcel.generic.MethodGen;
30  import org.apache.bcel.generic.RET;
31  import org.apache.bcel.generic.ReturnInstruction;
32  import org.apache.bcel.generic.Select;
33  import org.apache.bcel.verifier.exc.AssertionViolatedException;
34  import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
35  
36  
37  /**
38   * This class represents a control flow graph of a method.
39   * 
40   * WARNING! These classes are a fork of the bcel verifier. 
41   *
42   * @version $Id: ControlFlowGraph.java 480487 2006-11-29 08:54:42Z bayard $
43   * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A>
44   */
45  public class ControlFlowGraph{
46  
47  	/**
48  	 * Objects of this class represent a node in a ControlFlowGraph.
49  	 * These nodes are instructions, not basic blocks.
50  	 */
51  	private class InstructionContextImpl implements InstructionContext{
52  
53  		/**
54  		 * The InstructionHandle this InstructionContext is wrapped around.
55  		 */
56  		private InstructionHandle instruction;
57  
58  		/**
59  		 * The 'incoming' execution Frames.
60  		 */
61  		private HashMap inFrames;	// key: the last-executed JSR
62  
63  		/**
64  		 * The 'outgoing' execution Frames.
65  		 */
66  		private HashMap outFrames; // key: the last-executed JSR 
67  
68  		/**
69  		 * The 'execution predecessors' - a list of {@link InstructionContext}
70  		 * of those instances that have been execute()d before in that order.
71  		 */
72  		private ExecutionPath executionPredecessors = null; // Type: InstructionContext
73  	
74  		/**
75  		 * Creates an InstructionHandleImpl object from an InstructionHandle.
76  		 * Creation of one per InstructionHandle suffices. Don't create more.
77  		 */
78  		public InstructionContextImpl(InstructionHandle inst){
79  			if (inst == null) throw new AssertionViolatedException("Cannot instantiate InstructionContextImpl from NULL.");
80  		
81  			instruction = inst;
82  			inFrames = new java.util.HashMap();
83  			outFrames = new java.util.HashMap();
84  		}
85  
86  		/**
87  		 * Returns the exception handlers of this instruction.
88  		 */
89  		public ExceptionHandler[] getExceptionHandlers(){
90  			return exceptionhandlers.getExceptionHandlers(getInstruction());
91  		}
92  
93  		/**
94  		 * Returns a clone of the "outgoing" frame situation with respect to the given ExecutionChain.
95  		 */	
96  		public Frame getOutFrame(ExecutionPath execChain){
97  			executionPredecessors = execChain;
98  
99  			Frame org;
100 
101 			InstructionContext jsr = lastExecutionJSR();
102 
103 			org = (Frame) outFrames.get(jsr);
104 
105 			if (org == null){
106 				throw new AssertionViolatedException("outFrame not set! This:\n"+this+"\nExecutionChain: "+getExecutionChain()+"\nOutFrames: '"+outFrames+"'.");
107 			}
108 			return org.getClone();
109 		}
110 
111     public Frame getInFrame() {
112 		  Frame org;
113 			
114 			InstructionContext jsr = lastExecutionJSR();
115 			
116 			org = (Frame) inFrames.get(jsr);
117 
118 			if (org == null){
119 			    throw new AssertionViolatedException("inFrame not set! This:\n"+this+"\nInFrames: '"+inFrames+"'.");
120       }
121       return org.getClone();
122     }
123 
124 		/**
125 		 * "Merges in" (vmspec2, page 146) the "incoming" frame situation;
126 		 * executes the instructions symbolically
127 		 * and therefore calculates the "outgoing" frame situation.
128 		 * Returns: True iff the "incoming" frame situation changed after
129 		 * merging with "inFrame".
130 		 * The execPreds contain the InstructionContext
131 		 * objects executed so far in the correct order. This is just
132 		 * one execution path [out of many]. This is needed to correctly
133 		 * "merge" in the special case of a RET's successor.
134 		 * <B>The InstConstraintVisitor and ExecutionVisitor instances
135 		 * must be set up correctly.</B>
136 		 * @return true - if and only if the "outgoing" frame situation
137 		 * changed from the one before execute()ing.
138 		 */
139 		public boolean execute(Frame inFrame, ExecutionPath execPreds, ExecutionVisitor ev){
140 
141             // When merge failed, this is useful to see what are two passes
142             ExecutionPath oldExecPreds = executionPredecessors;
143 
144             executionPredecessors = execPreds;
145 
146 			//sanity check
147 			if ( (lastExecutionJSR() == null) && (subroutines.subroutineOf(getInstruction()) != subroutines.getTopLevel() ) ){
148 				//throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?");
149 				return false;
150 			}
151 			if ( (lastExecutionJSR() != null) && (subroutines.subroutineOf(getInstruction()) == subroutines.getTopLevel() ) ){
152 				//throw new AssertionViolatedException("Huh?! Am I '"+this+"' part of a subroutine or not?");
153 				return false;
154 			}
155 
156 			Frame inF = (Frame) inFrames.get(lastExecutionJSR());
157 			if (inF == null){// no incoming frame was set, so set it.
158 				inFrames.put(lastExecutionJSR(), inFrame);
159 				inF = inFrame;
160 			}
161 			else{// if there was an "old" inFrame
162 				if (inF.equals(inFrame)){ //shortcut: no need to merge equal frames.
163 					return false;
164 				}
165 				if (! mergeInFrames(inFrame)){
166 					return false;
167 				}
168 			}
169 			
170 			// Now we're sure the inFrame has changed!
171 			
172 			// new inFrame is already merged in, see above.		
173 			Frame workingFrame = inF.getClone();
174 
175 			// This executes the Instruction.
176 			// Therefore the workingFrame object is modified.
177 //ExecutionVisitor ev = ExecutionVisitor.getInstance(VerifierFactory.getVerifier(method_gen.getClassName()));
178 			ev.setFrame(workingFrame);
179 			getInstruction().accept(ev);
180 			//getInstruction().accept(ExecutionVisitor.withFrame(workingFrame));
181 			outFrames.put(lastExecutionJSR(), workingFrame);
182 
183 			return true;	// new inFrame was different from old inFrame so merging them
184 										// yielded a different this.inFrame.
185 		}
186 
187 		/**
188 		 * Returns a simple String representation of this InstructionContext.
189 		 */
190 		public String toString(){
191 		//TODO: Put information in the brackets, e.g.
192 		//      Is this an ExceptionHandler? Is this a RET? Is this the start of
193 		//      a subroutine?
194 			String ret = getInstruction().toString(false)+"\t[InstructionContext]";
195 			return ret;
196 		}
197 
198 		/**
199 		 * Does the actual merging (vmspec2, page 146).
200 		 * Returns true IFF this.inFrame was changed in course of merging with inFrame.
201 		 */
202 		private boolean mergeInFrames(Frame inFrame){
203 			// TODO: Can be performance-improved.
204 			Frame inF = (Frame) inFrames.get(lastExecutionJSR());
205 			OperandStack oldstack = inF.getStack().getClone();
206 			LocalVariables oldlocals = inF.getLocals().getClone();
207 			try{
208 				inF.getStack().merge(inFrame.getStack());
209 				inF.getLocals().merge(inFrame.getLocals());
210 			}
211 			catch (StructuralCodeConstraintException sce){
212 				extendMessageWithFlow(sce);
213 				throw sce;
214 			}
215 			if (	oldstack.equals(inF.getStack()) &&
216 						oldlocals.equals(inF.getLocals()) ){
217 				return false;
218 			}
219 			else{
220 				return true;
221 			}
222 		}
223 
224 		/**
225 		 * Returns the control flow execution chain. This is built
226 		 * while {@link #execute(Frame, ExecutionPath, ExecutionVisitor) executing} the code represented
227 		 * by the surrounding ControlFlowGraph.
228 		 */
229 		private String getExecutionChain(){
230             return executionPredecessors.toString()+"\n"+this.toString();
231 		}
232 
233 
234 		/**
235 		 * Extends the StructuralCodeConstraintException ("e") object with an at-the-end-extended message.
236 		 * This extended message will then reflect the execution flow needed to get to the constraint
237 		 * violation that triggered the throwing of the "e" object.
238 		 */
239 		private void extendMessageWithFlow(StructuralCodeConstraintException e){
240 			String s = "Execution flow:\n";
241 			e.extendMessage("", s+getExecutionChain());
242 		}
243 
244 		/*
245 		 * Fulfils the contract of InstructionContext.getInstruction().
246 		 */
247 		public InstructionHandle getInstruction(){
248 			return instruction;
249 		}
250 
251 		private InstructionContext lastExecutionJSR(){
252             return executionPredecessors.lastExecutionJSR();
253 		}
254 
255 		/* Satisfies InstructionContext.getSuccessors(). */
256 		public InstructionContext[] getSuccessors(){
257 			return contextsOf(_getSuccessors());
258 		}
259 
260 		/**
261 		 * A utility method that calculates the successors of a given InstructionHandle
262 		 * That means, a RET does have successors as defined here.
263 		 * A JsrInstruction has its target as its successor
264 		 * (opposed to its physical successor) as defined here.
265 		 */
266 // TODO: implement caching!
267 		private InstructionHandle[] _getSuccessors(){
268 			final InstructionHandle[] empty = new InstructionHandle[0];
269 			final InstructionHandle[] single = new InstructionHandle[1];
270 			final InstructionHandle[] pair = new InstructionHandle[2];
271 		
272 			Instruction inst = getInstruction().getInstruction();
273 		
274 			if (inst instanceof RET){
275 				Subroutine s = subroutines.subroutineOf(getInstruction());
276 				if (s==null){ //return empty; // RET in dead code. "empty" would be the correct answer, but we know something about the surrounding project...
277 					throw new AssertionViolatedException("Asking for successors of a RET in dead code?!");
278 				}
279 //TODO: remove
280 throw new AssertionViolatedException("DID YOU REALLY WANT TO ASK FOR RET'S SUCCS?");
281 /*
282 				InstructionHandle[] jsrs = s.getEnteringJsrInstructions();
283 				InstructionHandle[] ret = new InstructionHandle[jsrs.length];
284 				for (int i=0; i<jsrs.length; i++){
285 					ret[i] = jsrs[i].getNext();
286 				}
287 				return ret;
288 */
289 			}
290 		
291 			// Terminates method normally.
292 			if (inst instanceof ReturnInstruction){
293 				return empty;
294 			}
295 		
296 			// Terminates method abnormally, because JustIce mandates
297 			// subroutines not to be protected by exception handlers.
298 			if (inst instanceof ATHROW){
299 				return empty;
300 			}
301 		
302 			// See method comment.
303 			if (inst instanceof JsrInstruction){
304 				single[0] = ((JsrInstruction) inst).getTarget();
305 				return single;
306 			}
307 
308 			if (inst instanceof GotoInstruction){
309 				single[0] = ((GotoInstruction) inst).getTarget();
310 				return single;
311 			}
312 
313 			if (inst instanceof BranchInstruction){
314 				if (inst instanceof Select){
315 					// BCEL's getTargets() returns only the non-default targets,
316 					// thanks to Eli Tilevich for reporting.
317 					InstructionHandle[] matchTargets = ((Select) inst).getTargets();
318 					InstructionHandle[] ret = new InstructionHandle[matchTargets.length+1];
319 					ret[0] = ((Select) inst).getTarget();
320 					System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
321 					return ret;
322 				}
323 				else{
324 					pair[0] = getInstruction().getNext();
325 					pair[1] = ((BranchInstruction) inst).getTarget();
326 					return pair;
327 				}
328 			}
329 
330 			// default case: Fall through.		
331 			single[0] = getInstruction().getNext();
332 			return single;
333 		}
334 
335 	} // End Inner InstructionContextImpl Class.
336 
337 	/** The Subroutines object for the method whose control flow is represented by this ControlFlowGraph. */
338 	private final Subroutines subroutines;
339 
340 	/** The ExceptionHandlers object for the method whose control flow is represented by this ControlFlowGraph. */
341 	private final ExceptionHandlers exceptionhandlers;
342 
343 	/** All InstructionContext instances of this ControlFlowGraph. */
344 	private Hashtable instructionContexts = new Hashtable(); //keys: InstructionHandle, values: InstructionContextImpl
345 
346 	/** 
347 	 * A Control Flow Graph.
348 	 */
349 	public ControlFlowGraph(MethodGen method_gen){
350 		subroutines = new Subroutines(method_gen);
351 		exceptionhandlers = new ExceptionHandlers(method_gen);
352 
353 		InstructionHandle[] instructionhandles = method_gen.getInstructionList().getInstructionHandles();
354 		for (int i=0; i<instructionhandles.length; i++){
355 			instructionContexts.put(instructionhandles[i], new InstructionContextImpl(instructionhandles[i]));
356 		}
357 	}
358 
359 	/**
360 	 * Returns the InstructionContext of a given instruction.
361 	 */
362 	public InstructionContext contextOf(InstructionHandle inst){
363 		InstructionContext ic = (InstructionContext) instructionContexts.get(inst);
364 		if (ic == null){
365 			throw new AssertionViolatedException("InstructionContext requested for an InstructionHandle that's not known!");
366 		}
367 		return ic;
368 	}
369 
370 	/**
371 	 * Returns the InstructionContext[] of a given InstructionHandle[],
372 	 * in a naturally ordered manner.
373 	 */
374 	public InstructionContext[] contextsOf(InstructionHandle[] insts){
375 		InstructionContext[] ret = new InstructionContext[insts.length];
376 		for (int i=0; i<insts.length; i++){
377 			ret[i] = contextOf(insts[i]);
378 		}
379 		return ret;
380 	}
381 
382 	/**
383 	 * Returns an InstructionContext[] with all the InstructionContext instances
384 	 * for the method whose control flow is represented by this ControlFlowGraph
385 	 * <B>(NOT ORDERED!)</B>.
386 	 */
387 	public InstructionContext[] getInstructionContexts(){
388 		InstructionContext[] ret = new InstructionContext[instructionContexts.values().size()];
389 		return (InstructionContext[]) instructionContexts.values().toArray(ret);
390 	}
391 
392 	/**
393 	 * Returns true, if and only if the said instruction is not reachable; that means,
394 	 * if it is not part of this ControlFlowGraph.
395 	 */
396 	public boolean isDead(InstructionHandle i){
397         return subroutines.subroutineOf(i) == null;
398 	}	 
399 }