1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
40
41 public class ControlFlowGraph {
42
43
44
45
46 private final class InstructionContextImpl implements InstructionContext {
47
48
49
50
51
52
53
54 private int TAG;
55
56
57
58
59 private final InstructionHandle instruction;
60
61
62
63
64 private final Map<InstructionContext, Frame> inFrames;
65
66
67
68
69 private final Map<InstructionContext, Frame> outFrames;
70
71
72
73
74
75 private List<InstructionContext> executionPredecessors;
76
77
78
79
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
93
94
95
96
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) {
105
106 throw new AssertionViolatedException("Asking for successors of a RET in dead code?!");
107 }
108
109
110
111
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
120
121
122 if (inst instanceof ReturnInstruction || inst instanceof ATHROW) {
123 return InstructionHandle.EMPTY_ARRAY;
124 }
125
126
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
140
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
154 single[0] = getInstruction().getNext();
155 return single;
156 }
157
158
159
160
161
162
163
164
165
166
167 @Override
168 public boolean execute(final Frame inFrame, final ArrayList<InstructionContext> execPreds, final InstConstraintVisitor icv, final ExecutionVisitor ev) {
169
170 @SuppressWarnings("unchecked")
171 final List<InstructionContext> clone = (List<InstructionContext>) execPreds.clone();
172 executionPredecessors = clone;
173
174
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) {
182 inFrames.put(lastExecutionJSR(), inFrame);
183 inF = inFrame;
184 } else if (inF.equals(inFrame) || !mergeInFrames(inFrame)) {
185 return false;
186 }
187
188
189
190
191 final Frame workingFrame = inF.getClone();
192
193 try {
194
195
196
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
207
208
209 ev.setFrame(workingFrame);
210 getInstruction().accept(ev);
211
212 outFrames.put(lastExecutionJSR(), workingFrame);
213
214 return true;
215
216 }
217
218
219
220
221
222
223 private void extendMessageWithFlow(final StructuralCodeConstraintException e) {
224 final String s = "Execution flow:\n";
225 e.extendMessage("", s + getExecutionChain());
226 }
227
228
229
230
231 @Override
232 public ExceptionHandler[] getExceptionHandlers() {
233 return exceptionhandlers.getExceptionHandlers(getInstruction());
234 }
235
236
237
238
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
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
299
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
324
325
326 private boolean mergeInFrames(final Frame inFrame) {
327
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
342 @Override
343 public void setTag(final int tag) {
344 TAG = tag;
345 }
346
347
348
349
350 @Override
351 public String toString() {
352
353
354
355 return getInstruction().toString(false) + "\t[InstructionContext]";
356 }
357
358 }
359
360
361
362
363
364 private final Subroutines subroutines;
365
366
367 private final ExceptionHandlers exceptionhandlers;
368
369
370 private final Map<InstructionHandle, InstructionContext> instructionContexts = new HashMap<>();
371
372
373
374
375
376
377 public ControlFlowGraph(final MethodGen methodGen) {
378 this(methodGen, true);
379 }
380
381
382
383
384
385
386
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
398 }
399
400
401
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
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
422
423
424 public InstructionContext[] getInstructionContexts() {
425 final InstructionContext[] ret = new InstructionContext[instructionContexts.size()];
426 return instructionContexts.values().toArray(ret);
427 }
428
429
430
431
432
433 public boolean isDead(final InstructionHandle i) {
434 return subroutines.subroutineOf(i) == null;
435 }
436 }