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