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