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