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