1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.bcel.verifier.structurals;
20
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.bcel.generic.ASTORE;
29 import org.apache.bcel.generic.ATHROW;
30 import org.apache.bcel.generic.BranchInstruction;
31 import org.apache.bcel.generic.CodeExceptionGen;
32 import org.apache.bcel.generic.GotoInstruction;
33 import org.apache.bcel.generic.IndexedInstruction;
34 import org.apache.bcel.generic.Instruction;
35 import org.apache.bcel.generic.InstructionHandle;
36 import org.apache.bcel.generic.JsrInstruction;
37 import org.apache.bcel.generic.LocalVariableInstruction;
38 import org.apache.bcel.generic.MethodGen;
39 import org.apache.bcel.generic.RET;
40 import org.apache.bcel.generic.ReturnInstruction;
41 import org.apache.bcel.generic.Select;
42 import org.apache.bcel.verifier.exc.AssertionViolatedException;
43 import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class Subroutines {
61
62 private enum ColourConstants {
63 WHITE, GRAY, BLACK
64 }
65
66
67
68
69 private final class SubroutineImpl implements Subroutine {
70
71
72
73
74
75 private static final int UNSET = -1;
76
77 private final SubroutineImpl[] EMPTY_ARRAY = {};
78
79
80
81
82
83 private int localVariable = UNSET;
84
85
86 private final Set<InstructionHandle> instructions = new HashSet<>();
87
88
89
90
91 private final Set<InstructionHandle> theJSRs = new HashSet<>();
92
93
94
95
96 private InstructionHandle theRET;
97
98
99
100
101 SubroutineImpl() {
102
103 }
104
105
106
107
108 public void addEnteringJsrInstruction(final InstructionHandle jsrInst) {
109 if (jsrInst == null || !(jsrInst.getInstruction() instanceof JsrInstruction)) {
110 throw new AssertionViolatedException("Expecting JsrInstruction InstructionHandle.");
111 }
112 if (localVariable == UNSET) {
113 throw new AssertionViolatedException("Set the localVariable first!");
114 }
115
116
117
118 if (localVariable != ((ASTORE) ((JsrInstruction) jsrInst.getInstruction()).getTarget().getInstruction()).getIndex()) {
119 throw new AssertionViolatedException("Setting a wrong JsrInstruction.");
120 }
121 theJSRs.add(jsrInst);
122 }
123
124
125
126
127
128
129 void addInstruction(final InstructionHandle ih) {
130 if (theRET != null) {
131 throw new AssertionViolatedException("All instructions must have been added before invoking setLeavingRET().");
132 }
133 instructions.add(ih);
134 }
135
136
137
138
139 @Override
140 public boolean contains(final InstructionHandle inst) {
141 return instructions.contains(inst);
142 }
143
144
145
146
147 @Override
148 public int[] getAccessedLocalsIndices() {
149
150 final Set<Integer> acc = new HashSet<>();
151 if (theRET == null && this != getTopLevel()) {
152 throw new AssertionViolatedException("This subroutine object must be built up completely before calculating accessed locals.");
153 }
154 {
155 for (final InstructionHandle ih : instructions) {
156
157 if (ih.getInstruction() instanceof LocalVariableInstruction || ih.getInstruction() instanceof RET) {
158 final int idx = ((IndexedInstruction) ih.getInstruction()).getIndex();
159 acc.add(Integer.valueOf(idx));
160
161 try {
162
163
164 if (ih.getInstruction() instanceof LocalVariableInstruction) {
165 final int s = ((LocalVariableInstruction) ih.getInstruction()).getType(null).getSize();
166 if (s == 2) {
167 acc.add(Integer.valueOf(idx + 1));
168 }
169 }
170 } catch (final RuntimeException re) {
171 throw new AssertionViolatedException("BCEL did not like NULL as a ConstantPoolGen object.", re);
172 }
173 }
174 }
175 }
176
177 {
178 final int[] ret = new int[acc.size()];
179 int j = -1;
180 for (final Integer accessedLocal : acc) {
181 j++;
182 ret[j] = accessedLocal.intValue();
183 }
184 return ret;
185 }
186 }
187
188
189
190
191 @Override
192 public InstructionHandle[] getEnteringJsrInstructions() {
193 if (this == getTopLevel()) {
194 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
195 }
196 return theJSRs.toArray(InstructionHandle.EMPTY_ARRAY);
197 }
198
199
200
201
202 @Override
203 public InstructionHandle[] getInstructions() {
204 return instructions.toArray(InstructionHandle.EMPTY_ARRAY);
205 }
206
207
208
209
210 @Override
211 public InstructionHandle getLeavingRET() {
212 if (this == getTopLevel()) {
213 throw new AssertionViolatedException("getLeavingRET() called on top level pseudo-subroutine.");
214 }
215 return theRET;
216 }
217
218
219 @Override
220 public int[] getRecursivelyAccessedLocalsIndices() {
221 final Set<Integer> s = new HashSet<>();
222 final int[] lvs = getAccessedLocalsIndices();
223 for (final int lv : lvs) {
224 s.add(Integer.valueOf(lv));
225 }
226 getRecursivelyAccessedLocalsIndicesHelper(s, subSubs());
227 final int[] ret = new int[s.size()];
228 int j = -1;
229 for (final Integer index : s) {
230 j++;
231 ret[j] = index.intValue();
232 }
233 return ret;
234 }
235
236
237
238
239
240
241 private void getRecursivelyAccessedLocalsIndicesHelper(final Set<Integer> set, final Subroutine[] subs) {
242 for (final Subroutine sub : subs) {
243 final int[] lvs = sub.getAccessedLocalsIndices();
244 for (final int lv : lvs) {
245 set.add(Integer.valueOf(lv));
246 }
247 if (sub.subSubs().length != 0) {
248 getRecursivelyAccessedLocalsIndicesHelper(set, sub.subSubs());
249 }
250 }
251 }
252
253
254
255
256
257 void setLeavingRET() {
258 if (localVariable == UNSET) {
259 throw new AssertionViolatedException("setLeavingRET() called for top-level 'subroutine' or forgot to set local variable first.");
260 }
261 InstructionHandle ret = null;
262 for (final InstructionHandle actual : instructions) {
263 if (actual.getInstruction() instanceof RET) {
264 if (ret != null) {
265 throw new StructuralCodeConstraintException("Subroutine with more then one RET detected: '" + ret + "' and '" + actual + "'.");
266 }
267 ret = actual;
268 }
269 }
270 if (ret == null) {
271 throw new StructuralCodeConstraintException("Subroutine without a RET detected.");
272 }
273 if (((RET) ret.getInstruction()).getIndex() != localVariable) {
274 throw new StructuralCodeConstraintException(
275 "Subroutine uses '" + ret + "' which does not match the correct local variable '" + localVariable + "'.");
276 }
277 theRET = ret;
278 }
279
280
281
282
283
284 void setLocalVariable(final int i) {
285 if (localVariable != UNSET) {
286 throw new AssertionViolatedException("localVariable set twice.");
287 }
288 localVariable = i;
289 }
290
291
292
293
294 @Override
295 public Subroutine[] subSubs() {
296 final Set<Subroutine> h = new HashSet<>();
297
298 for (final InstructionHandle ih : instructions) {
299 final Instruction inst = ih.getInstruction();
300 if (inst instanceof JsrInstruction) {
301 final InstructionHandle targ = ((JsrInstruction) inst).getTarget();
302 h.add(getSubroutine(targ));
303 }
304 }
305 return h.toArray(EMPTY_ARRAY);
306 }
307
308
309
310
311
312 @Override
313 public String toString() {
314 final StringBuilder ret = new StringBuilder();
315 ret.append("Subroutine: Local variable is '").append(localVariable);
316 ret.append("', JSRs are '").append(theJSRs);
317 ret.append("', RET is '").append(theRET);
318 ret.append("', Instructions: '").append(instructions).append("'.");
319
320 ret.append(" Accessed local variable slots: '");
321 int[] alv = getAccessedLocalsIndices();
322 for (final int element : alv) {
323 ret.append(element);
324 ret.append(" ");
325 }
326 ret.append("'.");
327
328 ret.append(" Recursively (via subsub...routines) accessed local variable slots: '");
329 alv = getRecursivelyAccessedLocalsIndices();
330 for (final int element : alv) {
331 ret.append(element);
332 ret.append(" ");
333 }
334 ret.append("'.");
335
336 return ret.toString();
337 }
338
339 }
340
341
342
343
344
345
346 private static InstructionHandle[] getSuccessors(final InstructionHandle instruction) {
347 final InstructionHandle[] single = new InstructionHandle[1];
348
349 final Instruction inst = instruction.getInstruction();
350
351
352
353
354 if (inst instanceof RET || inst instanceof ReturnInstruction || inst instanceof ATHROW) {
355 return InstructionHandle.EMPTY_ARRAY;
356 }
357
358
359 if (inst instanceof JsrInstruction) {
360 single[0] = instruction.getNext();
361 return single;
362 }
363
364 if (inst instanceof GotoInstruction) {
365 single[0] = ((GotoInstruction) inst).getTarget();
366 return single;
367 }
368
369 if (inst instanceof BranchInstruction) {
370 if (inst instanceof Select) {
371
372
373 final InstructionHandle[] matchTargets = ((Select) inst).getTargets();
374 final InstructionHandle[] ret = new InstructionHandle[matchTargets.length + 1];
375 ret[0] = ((Select) inst).getTarget();
376 System.arraycopy(matchTargets, 0, ret, 1, matchTargets.length);
377 return ret;
378 }
379 final InstructionHandle[] pair = new InstructionHandle[2];
380 pair[0] = instruction.getNext();
381 pair[1] = ((BranchInstruction) inst).getTarget();
382 return pair;
383 }
384
385
386 single[0] = instruction.getNext();
387 return single;
388 }
389
390
391
392
393
394 private final Map<InstructionHandle, Subroutine> subroutines = new HashMap<>();
395
396
397
398
399
400
401 public final Subroutine TOPLEVEL;
402
403
404
405
406
407
408
409
410 public Subroutines(final MethodGen mg) {
411 this(mg, true);
412 }
413
414
415
416
417
418
419
420
421 public Subroutines(final MethodGen mg, final boolean enableJustIceCheck) {
422 final InstructionHandle[] all = mg.getInstructionList().getInstructionHandles();
423 final CodeExceptionGen[] handlers = mg.getExceptionHandlers();
424
425
426 TOPLEVEL = new SubroutineImpl();
427
428
429 final Set<InstructionHandle> subLeaders = new HashSet<>();
430 for (final InstructionHandle element : all) {
431 final Instruction inst = element.getInstruction();
432 if (inst instanceof JsrInstruction) {
433 subLeaders.add(((JsrInstruction) inst).getTarget());
434 }
435 }
436
437
438 for (final InstructionHandle astore : subLeaders) {
439 final SubroutineImpl sr = new SubroutineImpl();
440 sr.setLocalVariable(((ASTORE) astore.getInstruction()).getIndex());
441 subroutines.put(astore, sr);
442 }
443
444
445 subroutines.put(all[0], TOPLEVEL);
446 subLeaders.add(all[0]);
447
448
449
450
451
452
453 for (final InstructionHandle element : all) {
454 final Instruction inst = element.getInstruction();
455 if (inst instanceof JsrInstruction) {
456 final InstructionHandle leader = ((JsrInstruction) inst).getTarget();
457 ((SubroutineImpl) getSubroutine(leader)).addEnteringJsrInstruction(element);
458 }
459 }
460
461
462
463
464 final Set<InstructionHandle> instructionsAssigned = new HashSet<>();
465
466
467 final Map<InstructionHandle, ColourConstants> colors = new HashMap<>();
468
469 final List<InstructionHandle> qList = new ArrayList<>();
470 for (final InstructionHandle actual : subLeaders) {
471
472
473 for (final InstructionHandle element : all) {
474 colors.put(element, ColourConstants.WHITE);
475 }
476 colors.put(actual, ColourConstants.GRAY);
477
478
479 qList.clear();
480 qList.add(actual);
481
482
483
484
485
486 if (actual == all[0]) {
487 for (final CodeExceptionGen handler : handlers) {
488 colors.put(handler.getHandlerPC(), ColourConstants.GRAY);
489 qList.add(handler.getHandlerPC());
490 }
491 }
492
493
494
495 while (!qList.isEmpty()) {
496 final InstructionHandle u = qList.remove(0);
497 final InstructionHandle[] successors = getSuccessors(u);
498 for (final InstructionHandle successor : successors) {
499 if (colors.get(successor) == ColourConstants.WHITE) {
500 colors.put(successor, ColourConstants.GRAY);
501 qList.add(successor);
502 }
503 }
504 colors.put(u, ColourConstants.BLACK);
505 }
506
507 for (final InstructionHandle element : all) {
508 if (colors.get(element) == ColourConstants.BLACK) {
509 ((SubroutineImpl) (actual == all[0] ? getTopLevel() : getSubroutine(actual))).addInstruction(element);
510 if (instructionsAssigned.contains(element)) {
511 throw new StructuralCodeConstraintException(
512 "Instruction '" + element + "' is part of more than one subroutine (or of the top level and a subroutine).");
513 }
514 instructionsAssigned.add(element);
515 }
516 }
517 if (actual != all[0]) {
518 ((SubroutineImpl) getSubroutine(actual)).setLeavingRET();
519 }
520 }
521
522 if (enableJustIceCheck) {
523
524
525 for (final CodeExceptionGen handler : handlers) {
526 InstructionHandle protectedIh = handler.getStartPC();
527 while (protectedIh != handler.getEndPC().getNext()) {
528
529 for (final Subroutine sub : subroutines.values()) {
530 if (sub != subroutines.get(all[0]) && sub.contains(protectedIh)) {
531 throw new StructuralCodeConstraintException("Subroutine instruction '" + protectedIh + "' is protected by an exception handler, '"
532 + handler + "'. This is forbidden by the JustIce verifier due to its clear definition of subroutines.");
533 }
534 }
535 protectedIh = protectedIh.getNext();
536 }
537 }
538 }
539
540
541
542
543
544
545
546 noRecursiveCalls(getTopLevel(), new HashSet<>());
547
548 }
549
550
551
552
553
554
555
556 public Subroutine getSubroutine(final InstructionHandle leader) {
557 final Subroutine ret = subroutines.get(leader);
558
559 if (ret == null) {
560 throw new AssertionViolatedException("Subroutine requested for an InstructionHandle that is not a leader of a subroutine.");
561 }
562
563 if (ret == TOPLEVEL) {
564 throw new AssertionViolatedException("TOPLEVEL special subroutine requested; use getTopLevel().");
565 }
566
567 return ret;
568 }
569
570
571
572
573
574
575
576
577
578 public Subroutine getTopLevel() {
579 return TOPLEVEL;
580 }
581
582
583
584
585
586
587
588
589 private void noRecursiveCalls(final Subroutine sub, final Set<Integer> set) {
590 final Subroutine[] subs = sub.subSubs();
591
592 for (final Subroutine sub2 : subs) {
593 final int index = ((RET) sub2.getLeavingRET().getInstruction()).getIndex();
594
595 if (!set.add(Integer.valueOf(index))) {
596
597 final SubroutineImpl si = (SubroutineImpl) sub2;
598 throw new StructuralCodeConstraintException("Subroutine with local variable '" + si.localVariable + "', JSRs '" + si.theJSRs + "', RET '"
599 + si.theRET + "' is called by a subroutine which uses the same local variable index as itself; maybe even a recursive call?"
600 + " JustIce's clean definition of a subroutine forbids both.");
601 }
602
603 noRecursiveCalls(sub2, set);
604
605 set.remove(Integer.valueOf(index));
606 }
607 }
608
609
610
611
612
613
614
615
616
617 public Subroutine subroutineOf(final InstructionHandle any) {
618 for (final Subroutine s : subroutines.values()) {
619 if (s.contains(any)) {
620 return s;
621 }
622 }
623 System.err.println("DEBUG: Please verify '" + any.toString(true) + "' lies in dead code.");
624 return null;
625
626 }
627
628
629
630
631 @Override
632 public String toString() {
633 return "---\n" + subroutines + "\n---\n";
634 }
635 }