7a1d48db1ebd3e56a4d49d7eb251825ae42694a0
[jpf-core.git] / src / main / gov / nasa / jpf / vm / MethodInfo.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.vm;
19
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.JPFException;
23 import gov.nasa.jpf.util.JPFLogger;
24 import gov.nasa.jpf.util.LocationSpec;
25 import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
26 import java.lang.reflect.Modifier;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30
31
32 /**
33  * information associated with a method. Each method in JPF
34  * is represented by a MethodInfo object
35  */
36 public class MethodInfo extends InfoObject implements GenericSignatureHolder  {
37
38   static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.MethodInfo");
39   
40   static final int INIT_MTH_SIZE = 4096;
41   protected static final ArrayList<MethodInfo> mthTable = new ArrayList<MethodInfo>(INIT_MTH_SIZE);
42   
43   // special globalIds
44   static final int DIRECT_CALL = -1;
45
46   static final LocalVarInfo[] EMPTY = new LocalVarInfo[0];
47   
48   static final int[] EMPTY_INT = new int[0];
49   
50   /**
51    * Used to warn about local variable information.
52    */
53   protected static boolean warnedLocalInfo = false;
54   
55   //--- various JPF method attributes
56   static final int  EXEC_ATOMIC = 0x10000; // method executed atomically
57   static final int  EXEC_HIDDEN = 0x20000; // method hidden from path
58   static final int  FIREWALL    = 0x40000; // firewall any unhandled exceptionHandlers
59                                            // (turn into UnhandledException throws)
60   static final int  IS_CLINIT   = 0x80000;
61   static final int  IS_INIT     = 0x100000;
62   
63   static final int  IS_REFLECTION = 0x200000; // this is a reflection direct call
64   static final int  IS_DIRECT_CALL = 0x400000;
65   
66   /** a unique int assigned to this method */
67   protected int globalId = -1;
68
69   /**
70    * this is a lazy evaluated mangled name consisting of the name and
71    * arg type signature
72    */
73   protected String uniqueName;
74
75   /** Name of the method */
76   protected String name;
77
78   /** Signature of the method */
79   protected String signature;
80
81   /** Generic signature of the method */
82   protected String genericSignature;
83
84   /** Class the method belongs to */
85   protected ClassInfo ci;
86
87   /** Instructions associated with the method */
88   protected Instruction[] code;
89
90   /** JPFConfigException handlers */
91   protected ExceptionHandler[] exceptionHandlers;
92
93   /** classnames of checked exception thrown by the method */
94   protected String[] thrownExceptionClassNames;
95
96   /** Table used for line numbers 
97    * this assigns a line number to every instruction index, instead of 
98    * using an array of ranges. Assuming we have 2-3 insns per line on average,
99    * this should still require less memory than a reference array with associated
100    * range objects, and allows faster access of instruction line numbers, which
101    * we might need for location specs
102    */
103   protected int[] lineNumbers;
104   
105   /** Local variable information */
106   protected LocalVarInfo localVars[] = null;
107
108   /** Maximum number of local variables */
109   protected int maxLocals;
110
111   /** Maximum number of elements on the stack */
112   protected int maxStack;
113
114   /** null if we don't have any */
115   AnnotationInfo[][] parameterAnnotations;
116
117   //--- a batch of attributes
118   
119   /** the standard Java modifier attributes */
120   protected int modifiers;
121    
122   /** a batch of execution related JPF attributes */
123   protected int attributes;
124       
125
126   //--- all the stuff we need for native methods
127   // <2do> pcm - turn this into a derived class
128
129   /**  the number of stack slots for the arguments (incl. 'this'), lazy eval */
130   protected int argSize = -1;
131
132   /** number of arguments (excl. 'this'), lazy eval */
133   protected int nArgs = -1;
134
135   /** what return type do we have (again, lazy evaluated) */
136   protected byte returnType = -1;
137
138   /** number of stack slots for return value */
139   protected int retSize = -1;
140
141   /** used for native method parameter conversion (lazy evaluated) */
142   protected byte[] argTypes = null;
143   
144   static boolean init (Config config) {
145     mthTable.clear();    
146     return true;
147   }
148
149   public static MethodInfo getMethodInfo (int globalId){
150     if (globalId >=0 && globalId <mthTable.size()){
151       return mthTable.get(globalId);
152     } else {
153       return null;
154     }
155   }
156   
157   public static MethodInfo create (String name, String signature, int modifiers){
158     return new MethodInfo( name, signature, modifiers);
159   }
160   
161   public static MethodInfo create (ClassInfo ci, String name, String signature, int modifiers){
162     return new MethodInfo( ci, name, signature, modifiers);
163   }
164   
165   static MethodInfo create (ClassInfo ci, String name, String signature, int modifiers, int maxLocals, int maxStack){
166     return new MethodInfo( ci, name, signature, modifiers, maxLocals, maxStack);
167   }
168
169   /**
170    * for direct call construction
171    * Note: this is only a partial initialization, the code still has to be created/installed by the caller
172    */
173   public MethodInfo (MethodInfo callee, int nLocals, int nOperands) {
174     globalId = DIRECT_CALL;
175     // we don't want direct call methods in the mthTable (would be a memory leak) so don't register
176     
177     ci = callee.ci;
178     name = "[" + callee.name + ']'; // it doesn't allocate anything, so we don't have to be unique
179     signature = "()V";
180     genericSignature = "";
181     maxLocals = nLocals;
182     maxStack = nOperands;  // <2do> cache for optimization
183     localVars = EMPTY;
184     lineNumbers = null;
185     exceptionHandlers = null;
186     thrownExceptionClassNames = null;
187     uniqueName = name;
188     
189     // we need to preserve the ClassInfo so that class resolution for static method calls works
190     ci = callee.ci;
191     
192     attributes |= IS_DIRECT_CALL;
193     modifiers = Modifier.STATIC;   // always treated as static
194     
195     // code still has to be installed by caller
196   }
197   
198   /**
199    * This is used to create synthetic methods of function object types
200    */
201   public MethodInfo(String name, String signature, int modifiers, int nLocals, int nOperands) {
202     this( name, signature, modifiers);
203     maxLocals = nLocals;
204     maxStack = nOperands;
205     localVars = EMPTY;
206   }
207   
208   /**
209    * for NativeMethodInfo creation 
210    */
211   public MethodInfo (MethodInfo mi) {
212     globalId = mi.globalId;
213     uniqueName = mi.uniqueName;
214     name = mi.name;
215     signature = mi.signature;
216     genericSignature = mi.genericSignature;
217     ci = mi.ci;
218     modifiers = mi.modifiers;
219     attributes = mi.attributes;
220     thrownExceptionClassNames = mi.thrownExceptionClassNames;
221     parameterAnnotations = mi.parameterAnnotations;
222
223     annotations = mi.annotations;
224     
225     localVars = null; // there are no StackFrame localVarInfos, this is native
226     // code still has to be installed by caller
227   }
228   
229   // <2do> this is going away
230   public MethodInfo (ClassInfo ci, String name, String signature, int modifiers, int maxLocals, int maxStack){
231     this.ci = ci;
232     this.name = name;
233     this.signature = signature;
234     this.uniqueName = getUniqueName(name, signature);
235     this.genericSignature = "";
236     this.maxLocals = maxLocals;
237     this.maxStack = maxStack;
238     this.modifiers = modifiers;
239
240     this.lineNumbers = null;
241     this.exceptionHandlers = null;
242     this.thrownExceptionClassNames = null;
243
244     // set attributes we can deduce from the name and the ClassInfo
245     if (ci != null){
246       if (name.equals("<init>")) {
247         attributes |= IS_INIT;
248       } else if (name.equals("<clinit>")) {
249         this.modifiers |= Modifier.SYNCHRONIZED;
250         attributes |= IS_CLINIT | FIREWALL;
251       }
252       if (ci.isInterface()) { // all interface methods are public
253         this.modifiers |= Modifier.PUBLIC;
254       }
255     }
256
257     this.globalId = mthTable.size();
258     mthTable.add(this);
259   }
260
261   
262   public MethodInfo (String name, String signature, int modifiers){
263     this.name = name;
264     this.signature = signature;
265     this.modifiers = modifiers;
266     this.uniqueName = getUniqueName(name, signature);
267     this.genericSignature = "";
268
269     if (name.equals("<init>")) {
270       attributes |= IS_INIT;
271     } else if (name.equals("<clinit>")) {
272       // for some reason clinits don't have the synchronized modifier, but they are synchronized
273       // we keep it consistent so that we don't have to implement special lock acquisition/release for clinits
274       this.modifiers |= Modifier.SYNCHRONIZED;
275       attributes |= IS_CLINIT | FIREWALL;
276     }
277     
278     this.globalId = mthTable.size();
279     mthTable.add(this);    
280   }
281
282   public MethodInfo (ClassInfo ci, String name, String signature, int modifiers){
283     this(name, signature, modifiers);
284     
285     this.ci = ci;
286   }
287   
288   //--- setters used during construction
289   
290   public void linkToClass (ClassInfo ci){
291     this.ci = ci;
292     
293     if (ci.isInterface()) { // all interface methods are public
294       this.modifiers |= Modifier.PUBLIC;
295     }
296   }
297   
298   public void setMaxLocals(int maxLocals){
299     this.maxLocals = maxLocals;
300   }
301
302   public void setMaxStack(int maxStack){
303     this.maxStack = maxStack;
304   }
305   
306   public void setCode (Instruction[] code){
307     for (int i=0; i<code.length; i++){
308       code[i].setMethodInfo(this);
309     }
310     this.code = code;
311   }
312   
313   
314   public boolean hasParameterAnnotations() {
315     return (parameterAnnotations != null);
316   }
317
318   // since some listeners might call this on every method invocation, we should do a little optimization
319   static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_0 = new AnnotationInfo[0][];
320   static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_1 = { new AnnotationInfo[0] };
321   static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_2 = { new AnnotationInfo[0], new AnnotationInfo[0] };
322   static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_3 = { new AnnotationInfo[0], new AnnotationInfo[0], new AnnotationInfo[0] };  
323   
324   public AnnotationInfo[][] getParameterAnnotations() {
325     if (parameterAnnotations == null){ // keep this similar to getAnnotations()
326       int n = getNumberOfArguments();
327       switch (n){
328       case 0: return NO_PARAMETER_ANNOTATIONS_0;
329       case 1: return NO_PARAMETER_ANNOTATIONS_1;
330       case 2: return NO_PARAMETER_ANNOTATIONS_2;
331       case 3: return NO_PARAMETER_ANNOTATIONS_3;
332       default:
333         AnnotationInfo[][] pai = new AnnotationInfo[n][];
334         for (int i=0; i<n; i++){
335           pai[i] = new AnnotationInfo[0];
336         }
337         return pai;
338       }
339       
340     } else {
341       return parameterAnnotations;
342     }
343   }
344
345   /**
346    * return annotations for parameterIndex
347    */
348   public AnnotationInfo[] getParameterAnnotations(int parameterIndex){
349     if (parameterAnnotations == null){
350       return null;
351     } else {
352       if (parameterIndex >= getNumberOfArguments()){
353         return null;
354       } else {
355         return parameterAnnotations[parameterIndex];
356       }
357     }
358   }
359
360
361   
362   public static int getNumberOfLoadedMethods () {
363     return mthTable.size();
364   }
365
366   void setAtomic (boolean isAtomic) {
367     if (isAtomic) {
368       attributes |= EXEC_ATOMIC;
369     } else {
370       attributes &= ~EXEC_ATOMIC;
371     }
372   }
373   public boolean isAtomic () {
374     return ((attributes & EXEC_ATOMIC) != 0);
375   }
376   
377   void setHidden (boolean isHidden) {
378     if (isHidden) {
379       attributes |= EXEC_HIDDEN;
380     } else {
381       attributes &= ~EXEC_HIDDEN;
382     }
383   }
384   public boolean isHidden () {
385     return ((attributes & EXEC_HIDDEN) != 0);    
386   }
387   
388   /**
389    * turn unhandled exceptionHandlers at the JPF execution level
390    * into UnhandledException throws at the host VM level
391    * this is useful to implement firewalls for direct calls
392    * which should not let exceptionHandlers permeate into bytecode/
393    * application code
394    */
395   public void setFirewall (boolean isFirewalled) {
396     if (isFirewalled) {
397       attributes |= FIREWALL;
398     } else {
399       attributes &= ~FIREWALL;
400     }
401   }
402   public boolean isFirewall () {
403     return ((attributes & FIREWALL) != 0);    
404   }
405   
406   
407   
408   @Override
409   public Object clone() {
410     try {
411       return super.clone();
412     } catch (CloneNotSupportedException cnx) {
413       return null;
414     }
415   }
416   
417   public int getGlobalId() {
418     return globalId;
419   }
420
421   public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti){
422     return ci.createRunStartStackFrame( ti, this);
423   }
424
425   public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, int nLocals){
426     return ci.createDirectCallStackFrame(ti, this, nLocals);
427   }
428
429   public boolean isSyncRelevant () {
430     return (name.charAt(0) != '<');
431   }
432   
433   public boolean isInitOrClinit (){
434     return ((attributes & (IS_CLINIT | IS_INIT)) != 0);
435   }
436   
437   public boolean isClinit () {
438     return ((attributes & IS_CLINIT) != 0);
439   }
440
441   public boolean isClinit (ClassInfo ci) {
442     return (((attributes & IS_CLINIT) != 0) && (this.ci == ci));
443   }
444
445   public boolean isInit() {
446     return ((attributes & IS_INIT) != 0);
447   }
448   
449   public boolean isDirectCallStub(){
450     return ((attributes & IS_DIRECT_CALL) != 0);    
451   }
452   
453   /**
454    * yet another name - this time with a non-mangled, but abbreviated signature
455    * and without return type (e.g. like "main(String[])"
456    */
457   public String getLongName () {
458     StringBuilder sb = new StringBuilder();
459     sb.append(name);
460     
461     sb.append('(');
462     String[] argTypeNames = getArgumentTypeNames();
463     for (int i=0; i<argTypeNames.length; i++) {
464       String a = argTypeNames[i];
465       int idx = a.lastIndexOf('.');
466       if (idx > 0) {
467         a = a.substring(idx+1);
468       }
469       if (i>0) {
470         sb.append(',');
471       }
472       sb.append(a);
473     }
474     sb.append(')');
475     
476     return sb.toString();
477   }
478   
479   /**
480    * return the minimal name that has to be unique for overloading
481    * used as a lookup key
482    * NOTE: with the silent introduction of covariant return types
483    * in Java 5.0, we have to use the full signature to be unique
484    */
485   public static String getUniqueName (String mname, String signature) {
486     return (mname + signature);
487   }
488
489   public String getStackTraceSource() {
490     return getSourceFileName();
491   }
492
493   public byte[] getArgumentTypes () {
494     if (argTypes == null) {
495       argTypes = Types.getArgumentTypes(signature);
496       nArgs = argTypes.length;
497     }
498
499     return argTypes;
500   }
501
502   public String[] getArgumentTypeNames () {
503     return Types.getArgumentTypeNames(signature);
504   }
505
506   // TODO: Fix for Groovy's model-checking
507   public String[] getArgumentGenericTypeNames () {
508     // TODO: We need to double check but for some reason Groovy has a type of generic signature with "<*>"
509     // TODO: in the class file.
510     if (genericSignature == null || genericSignature.equals("") || genericSignature.contains("<*>"))
511       return getArgumentTypeNames();
512     return Types.getArgumentTypeNames(genericSignature);
513   }
514
515   public int getArgumentsSize () {
516     if (argSize < 0) {
517       argSize = Types.getArgumentsSize(signature);
518
519       if (!isStatic()) {
520         argSize++;
521       }
522     }
523
524     return argSize;
525   }
526   
527   /**
528    * return only the LocalVarInfos for arguments, in order of definition
529    * or null if there are no localVarInfos.
530    * throw a JPFException if there are more immediately in scope vars than args
531    * 
532    * NOTE - it is perfectly legal for a method to have arguments but no LocalVarInfos,
533    * which are code attributes, clients have to check for a non-null return value
534    * even if the method has arguments.
535    * Note also that abstract / interface methods don't have code and hence no
536    * LocalVarInfos
537    */
538   public LocalVarInfo[] getArgumentLocalVars(){
539     if (localVars == null){ // shortcut in case we don't have args or localVars;
540       return null;
541     }
542     
543     int nArgs = getNumberOfStackArguments(); // we want 'this'
544     if (nArgs == 0){
545       return new LocalVarInfo[0]; // rare enough so that we don't use a static
546     }
547
548     LocalVarInfo[] argLvis = new LocalVarInfo[nArgs];
549     int n = 0; // how many args we've got so far
550     
551     for (LocalVarInfo lvi : localVars){
552       // arguments are the only ones that are immediately in scope
553       if (lvi.getStartPC() == 0){
554         if (n == nArgs){ // ARGH - more in-scope vars than args
555           throw new JPFException("inconsistent localVar table for method " + getFullName());
556         }
557         
558         // order with respect to slot index - since this might get called
559         // frequently, we don't use java.util.Arrays.sort() but sort in
560         // on-the-fly. Note that we can have several localVar entries for the
561         // same name, but only one can be immediately in scope
562         int slotIdx = lvi.getSlotIndex();
563
564         int i;
565         for (i = 0; i < n; i++) {
566           if (slotIdx < argLvis[i].getSlotIndex()) {
567             for (int j=n; j>i; j--){
568               argLvis[j] = argLvis[j-1];
569             }
570             argLvis[i] = lvi;
571             n++;
572             break;
573           }
574         }
575         if (i == n) { // append
576           argLvis[n++] = lvi;
577         }
578       }
579     }
580     
581     return argLvis;
582   }
583   
584   public String getReturnType () {
585     return Types.getReturnTypeSignature(signature);
586   }
587
588   public String getReturnTypeName () {
589     return Types.getReturnTypeName(signature);
590   }
591
592   public String getGenericReturnTypeName () {
593     // TODO: We need to double check but for some reason Groovy has a type of generic signature with "<*>"
594     // TODO: in the class file.
595     if (genericSignature == null || genericSignature.equals("") || genericSignature.contains("<*>"))
596       return Types.getReturnTypeName(signature);
597     return Types.getGenericReturnTypeName(genericSignature);
598   }
599
600   public String getSourceFileName () {
601     if (ci != null) {
602       return ci.getSourceFileName();
603     } else {
604       return "[VM]";
605     }
606   }
607
608   public String getClassName () {
609     if (ci != null) {
610       return ci.getName();
611     } else {
612       return "[VM]";
613     }
614   }
615   
616   /**
617    * Returns the class the method belongs to.
618    */
619   public ClassInfo getClassInfo () {
620     return ci;
621   }
622
623   /**
624    * @deprecated - use getFullName
625    */
626   @Deprecated
627 public String getCompleteName () {
628     return getFullName();
629   }
630
631   /**
632    * return classname.name (but w/o signature)
633    */
634   public String getBaseName() {
635     return getClassName() + '.' + name;
636   }
637     
638   public boolean isCtor () {
639     return (name.equals("<init>"));
640   }
641   
642   public boolean isInternalMethod () {
643     // <2do> pcm - should turn this into an attribute for efficiency reasons
644     return (name.equals("<clinit>") || uniqueName.equals("finalize()V"));
645   }
646   
647   public boolean isThreadEntry (ThreadInfo ti) {
648     return (uniqueName.equals("run()V") && (ti.countStackFrames() == 1));
649   }
650   
651   /**
652    * Returns the full classname (if any) + name + signature.
653    */
654   public String getFullName () {
655     if (ci != null) {
656       return ci.getName() + '.' + getUniqueName();
657     } else {
658       return getUniqueName();
659     }
660   }
661
662   /**
663    * returns stack trace name: classname (if any) + name
664    */
665   public String getStackTraceName(){
666     if (ci != null) {
667       return ci.getName() + '.' + name;
668     } else {
669       return name;
670     }
671   }
672   
673   /**
674    * return number of instructions
675    */
676   public int getNumberOfInstructions() {
677     if (code == null){
678       return 0;
679     }
680     
681     return code.length;
682   }
683   
684   /**
685    * Returns a specific instruction.
686    */
687   public Instruction getInstruction (int i) {
688     if (code == null) {
689       return null;
690     }
691
692     if ((i < 0) || (i >= code.length)) {
693       return null;
694     }
695
696     return code[i];
697   }
698
699   /**
700    * Returns the instruction at a certain position.
701    */
702   public Instruction getInstructionAt (int position) {
703     if (code == null) {
704       return null;
705     }
706
707     for (int i = 0, l = code.length; i < l; i++) {
708       if ((code[i] != null) && (code[i].getPosition() == position)) {
709         return code[i];
710       }
711     }
712
713     throw new JPFException("instruction not found");
714   }
715
716   /**
717    * Returns the instructions of the method.
718    */
719   public Instruction[] getInstructions () {
720     return code;
721   }
722   
723   public boolean includesLine (int line){
724     int len = code.length;
725     return (code[0].getLineNumber() <= line) && (code[len].getLineNumber() >= line);
726   }
727
728   public Instruction[] getInstructionsForLine (int line){
729     return getInstructionsForLineInterval(line,line);
730   }
731
732   public Instruction[] getInstructionsForLineInterval (int l1, int l2){
733     Instruction[] c = code;
734        
735     // instruction line numbers don't have to be monotonic (they can decrease for loops)
736     // hence we cannot easily check for overlapping ranges
737     
738     if (c != null){
739        ArrayList<Instruction> matchingInsns = null;
740        
741        for (int i = 0; i < c.length; i++) {
742         Instruction insn = c[i];
743         int line = insn.getLineNumber();
744         if (line == l1 || line == l2 || (line > l1 && line < l2)) {
745           if (matchingInsns == null) {
746             matchingInsns = new ArrayList<Instruction>();
747           }
748           matchingInsns.add(insn);
749         }
750       }
751       
752       if (matchingInsns == null) {
753         return null;
754       } else {
755         return matchingInsns.toArray(new Instruction[matchingInsns.size()]);
756       }
757             
758     } else {
759       return null;
760     }
761   }
762
763   public Instruction[] getMatchingInstructions (LocationSpec lspec){
764     return getInstructionsForLineInterval(lspec.getFromLine(), lspec.getToLine());
765   }
766
767
768   /**
769    * Returns the line number for a given position.
770    */
771   public int getLineNumber (Instruction pc) {
772     if (lineNumbers == null) {
773       if (pc == null)
774         return -1;
775       else
776         return pc.getPosition();
777     }
778
779     if (pc != null) {
780       int idx = pc.getInstructionIndex();
781       if (idx < 0) idx = 0;
782       return lineNumbers[idx];
783     } else {
784       return -1;
785     }
786   }
787
788   /**
789    * Returns a table to translate positions into line numbers.
790    */
791   public int[] getLineNumbers () {
792     return lineNumbers;
793   }
794
795   public boolean containsLineNumber (int n){
796     if (lineNumbers != null){
797       return (lineNumbers[0] <= n) && (lineNumbers[lineNumbers.length-1] <= n);
798     }
799     
800     return false;
801   }
802   
803   public boolean intersectsLineNumbers( int first, int last){
804     if (lineNumbers != null){
805       if ((last < lineNumbers[0]) || (first > lineNumbers[lineNumbers.length-1])){
806         return false;
807       }
808       return true;
809     }
810     
811     return false;
812   }
813   
814   public ExceptionHandler getHandlerFor (ClassInfo ciException, Instruction insn){
815     if (exceptionHandlers != null){
816       int position = insn.getPosition();
817       for (int i=0; i<exceptionHandlers.length; i++){
818         ExceptionHandler handler = exceptionHandlers[i];
819         if ((position >= handler.getBegin()) && (position < handler.getEnd())) {
820           // checks if this type of exception is caught here (null means 'any')
821           String handledType = handler.getName();
822           if ((handledType == null)   // a catch-all handler
823                   || ciException.isInstanceOf(handledType)) {
824             return handler;
825           }
826         }          
827       }      
828     }
829     
830     return null;
831   }
832   
833   public boolean isMJI () {
834     return false;
835   }
836
837   public int getMaxLocals () {
838     return maxLocals;
839   }
840
841   public int getMaxStack () {
842     return maxStack;
843   }
844
845   public ExceptionHandler[] getExceptions () {
846     return exceptionHandlers;
847   }
848
849   public String[] getThrownExceptionClassNames () {
850     return thrownExceptionClassNames;
851   }
852
853
854   public LocalVarInfo getLocalVar(String name, int pc){
855     LocalVarInfo[] vars = localVars;
856     if (vars != null){
857       for (int i = 0; i < vars.length; i++) {
858         LocalVarInfo lv = vars[i];
859         if (lv.matches(name, pc)) {
860           return lv;
861         }
862       }
863     }
864
865     return null;
866
867   }
868
869   public LocalVarInfo getLocalVar (int slotIdx, int pc){
870     LocalVarInfo[] vars = localVars;
871
872     if (vars != null){
873       for (int i = 0; i < vars.length; i++) {
874         LocalVarInfo lv = vars[i];
875         if (lv.matches(slotIdx, pc)) {
876           return lv;
877         }
878       }
879     }
880
881     return null;
882   }
883
884   public LocalVarInfo[] getLocalVars() {
885     return localVars; 
886   }
887
888
889   /**
890    * note that this might contain duplicates for variables with multiple
891    * scope entries
892    */
893   public String[] getLocalVariableNames() {
894     String[] names = new String[localVars.length];
895
896     for (int i=0; i<localVars.length; i++){
897       names[i] = localVars[i].getName();
898     }
899
900     return names;
901   }
902
903
904   public MethodInfo getOverriddenMethodInfo(){
905     MethodInfo smi = null;
906     
907     if (ci != null) {
908       ClassInfo sci = ci.getSuperClass();
909       if (sci != null){
910         smi = sci.getMethod(getUniqueName(), true);
911       }
912     }
913     
914     return smi;
915   }
916   
917   /**
918    * Returns the name of the method.
919    */
920   public String getName () {
921     return name;
922   }
923
924   public String getJNIName () {
925     return Types.getJNIMangledMethodName(null, name, signature);
926   }
927   
928   public int getModifiers () {
929     return modifiers;
930   }
931   
932   /**
933    * Returns true if the method is native
934    */
935   public boolean isNative () {
936     return ((modifiers & Modifier.NATIVE) != 0);
937   }
938
939   public boolean isAbstract () {
940     return ((modifiers & Modifier.ABSTRACT) != 0);
941   }
942   
943   // overridden by NativeMethodInfo
944   public boolean isUnresolvedNativeMethod(){
945     return ((modifiers & Modifier.NATIVE) != 0);
946   }
947
948   // overridden by NativeMethodInfo
949   public boolean isJPFExecutable (){
950     return !hasAttr(NoJPFExec.class);
951   }
952
953   public int getNumberOfArguments () {
954     if (nArgs < 0) {
955       nArgs = Types.getNumberOfArguments(signature);
956     }
957
958     return nArgs;
959   }
960
961   /**
962    * Returns the size of the arguments.
963    * This returns the number of parameters passed on the stack, incl. 'this'
964    */
965   public int getNumberOfStackArguments () {
966     int n = getNumberOfArguments();
967
968     return isStatic() ? n : n + 1;
969   }
970
971   public int getNumberOfCallerStackSlots () {
972     return Types.getNumberOfStackSlots(signature, isStatic()); // includes return type
973   }
974
975   public Instruction getFirstInsn(){
976     if (code != null){
977       return code[0];
978     }
979     return null;    
980   }
981   
982   public Instruction getLastInsn() {
983     if (code != null){
984       return code[code.length-1];
985     }
986     return null;
987   }
988
989   /**
990    * do we return Object references?
991    */
992   public boolean isReferenceReturnType () {
993     int r = getReturnTypeCode();
994
995     return ((r == Types.T_REFERENCE) || (r == Types.T_ARRAY));
996   }
997
998   public byte getReturnTypeCode () {
999     if (returnType < 0) {
1000       returnType = Types.getReturnBuiltinType(signature);
1001     }
1002
1003     return returnType;
1004   }
1005
1006   /**
1007    * what is the slot size of the return value
1008    */
1009   public int getReturnSize() {
1010     if (retSize == -1){
1011       switch (getReturnTypeCode()) {
1012         case Types.T_VOID:
1013           retSize = 0;
1014           break;
1015
1016         case Types.T_LONG:
1017         case Types.T_DOUBLE:
1018           retSize = 2;
1019           break;
1020
1021         default:
1022           retSize = 1;
1023           break;
1024       }
1025     }
1026
1027     return retSize;
1028   }
1029
1030   public Class<? extends ChoiceGenerator<?>> getReturnChoiceGeneratorType (){
1031     switch (getReturnTypeCode()){
1032       case Types.T_BOOLEAN:
1033         return BooleanChoiceGenerator.class;
1034
1035       case Types.T_BYTE:
1036       case Types.T_CHAR:
1037       case Types.T_SHORT:
1038       case Types.T_INT:
1039         return IntChoiceGenerator.class;
1040
1041       case Types.T_LONG:
1042         return LongChoiceGenerator.class;
1043
1044       case Types.T_FLOAT:
1045         return FloatChoiceGenerator.class;
1046
1047       case Types.T_DOUBLE:
1048         return DoubleChoiceGenerator.class;
1049
1050       case Types.T_ARRAY:
1051       case Types.T_REFERENCE:
1052       case Types.T_VOID:
1053         return ReferenceChoiceGenerator.class;
1054     }
1055
1056     return null;
1057   }
1058
1059   /**
1060    * Returns the signature of the method.
1061    */
1062   public String getSignature () {
1063     return signature;
1064   }
1065
1066   @Override
1067   public String getGenericSignature() {
1068     return genericSignature;
1069   }
1070
1071   @Override
1072   public void setGenericSignature(String sig){
1073     genericSignature = sig;
1074   }
1075
1076   /**
1077    * Returns true if the method is static.
1078    */
1079   public boolean isStatic () {
1080     return ((modifiers & Modifier.STATIC) != 0);
1081   }
1082
1083   /**
1084    * is this a public method
1085    */
1086   public boolean isPublic() {
1087     return ((modifiers & Modifier.PUBLIC) != 0);
1088   }
1089   
1090   public boolean isPrivate() {
1091     return ((modifiers & Modifier.PRIVATE) != 0);
1092   }
1093   
1094   public boolean isProtected() {
1095     return ((modifiers & Modifier.PROTECTED) != 0);
1096   }
1097
1098   /**
1099    * Returns true if the method is synchronized.
1100    */
1101   public boolean isSynchronized () {
1102     return ((modifiers & Modifier.SYNCHRONIZED) != 0);
1103   }
1104
1105   // <2do> these modifiers are still java.lang.reflect internal and not
1106   // supported by public Modifier methods, but since we want to keep this 
1107   // similar to the Method reflection and we get the modifiers from the
1108   // classfile we implement this with explicit values
1109   
1110   public boolean isSynthetic(){
1111     return ((modifiers & 0x00001000) != 0);    
1112   } 
1113   public boolean isVarargs(){
1114     return ((modifiers & 0x00000080) != 0);        
1115   }
1116   
1117   /*
1118    * is this from a classfile or was it created by JPF (and hence should not
1119    * be visible in stacktraces etc)
1120    */
1121   public boolean isJPFInternal(){
1122     // note this has a different meaning than Method.isSynthetic(), which
1123     // is defined in VM spec 4.7.8. What we mean here is that this MethodInfo
1124     // is not associated with any class (such as direct call MethodInfos), but
1125     // there might be more in the future
1126     return (ci == null);
1127   }
1128   
1129   public String getUniqueName () {
1130     return uniqueName;
1131   }
1132   
1133   public boolean hasCode(){
1134     return (code != null);
1135   }
1136   
1137   public boolean hasEmptyBody (){
1138     // only instruction is a return
1139     return (code.length == 1 && (code[0] instanceof ReturnInstruction));
1140   }
1141
1142
1143   //--- parameter annotations
1144   //<2do> these are going away
1145   protected void startParameterAnnotations(int annotationCount){
1146     parameterAnnotations = new AnnotationInfo[annotationCount][];
1147   }
1148   protected void setParameterAnnotations(int index, AnnotationInfo[] ai){
1149     parameterAnnotations[index] = ai;
1150   }
1151   protected void finishParameterAnnotations(){
1152     // nothing
1153   }
1154
1155   public void setParameterAnnotations (AnnotationInfo[][] parameterAnnotations){
1156     this.parameterAnnotations = parameterAnnotations;
1157   }
1158   
1159   //--- thrown exceptions
1160   //<2do> these are going away
1161   protected void startTrownExceptions (int exceptionCount){
1162     thrownExceptionClassNames = new String[exceptionCount];
1163   }
1164   protected void setException (int index, String exceptionType){
1165     thrownExceptionClassNames[index] = Types.getClassNameFromTypeName(exceptionType);
1166   }
1167   protected void finishThrownExceptions(){
1168     // nothing
1169   }
1170
1171   public void setThrownExceptions (String[] exceptions){
1172     thrownExceptionClassNames = exceptions;
1173   }
1174   
1175
1176   //--- exception handler table initialization
1177   //<2do> these are going away
1178   protected void startExceptionHandlerTable (int handlerCount){
1179     exceptionHandlers = new ExceptionHandler[handlerCount];
1180   }
1181   protected void setExceptionHandler (int index, int startPc, int endPc, int handlerPc, String catchType){
1182     exceptionHandlers[index] = new ExceptionHandler(catchType, startPc, endPc, handlerPc);
1183   }
1184   protected void finishExceptionHandlerTable(){
1185     // nothing
1186   }
1187
1188   public void setExceptionHandlers (ExceptionHandler[] handlers){
1189     exceptionHandlers = handlers;
1190   }
1191   
1192   //--- local var table initialization
1193   // <2do> these are going away
1194   protected void startLocalVarTable (int localVarCount){
1195     localVars = new LocalVarInfo[localVarCount];
1196   }
1197   protected void setLocalVar(int index, String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex){
1198     localVars[index] = new LocalVarInfo(varName, descriptor, "", scopeStartPc, scopeEndPc, slotIndex);
1199   }
1200   protected void finishLocalVarTable(){
1201     // nothing to do
1202   }
1203
1204   public void setLocalVarTable (LocalVarInfo[] locals){
1205     localVars = locals;
1206   }
1207   
1208   public void setLocalVarAnnotations (){
1209     if (localVars != null){
1210       for (VariableAnnotationInfo ai : getTargetTypeAnnotations(VariableAnnotationInfo.class)){
1211         for (int i = 0; i < ai.getNumberOfScopeEntries(); i++) {
1212           for (LocalVarInfo lv : localVars) {
1213             if (lv.getStartPC() == ai.getStartPC(i) && lv.getSlotIndex() == ai.getSlotIndex(i)) {
1214               lv.addTypeAnnotation(ai);
1215             }
1216           }
1217         }
1218       }
1219     }
1220   }
1221   
1222   public boolean hasTypeAnnotatedLocalVars (){
1223     if (localVars != null){
1224       for (LocalVarInfo lv : localVars){
1225         if (lv.hasTypeAnnotations()){
1226           return true;
1227         }
1228       }
1229     }
1230     
1231     return false;
1232   }
1233   
1234   public List<LocalVarInfo> getTypeAnnotatedLocalVars (){
1235     List<LocalVarInfo> list = null;
1236     
1237     if (localVars != null){
1238       for (LocalVarInfo lv : localVars){
1239         if (lv.hasTypeAnnotations()){
1240           if (list == null){
1241             list = new ArrayList<LocalVarInfo>();
1242           }
1243           list.add(lv);
1244         }
1245       }
1246     }
1247     
1248     if (list == null){
1249       list = Collections.emptyList();
1250     }
1251     
1252     return list;
1253   }
1254   
1255   public List<LocalVarInfo> getTypeAnnotatedLocalVars (String annotationClsName){
1256     List<LocalVarInfo> list = null;
1257     
1258     if (localVars != null){
1259       for (LocalVarInfo lv : localVars){
1260         AbstractTypeAnnotationInfo tai = lv.getTypeAnnotation(annotationClsName);
1261         if (tai != null){
1262           if (list == null){
1263             list = new ArrayList<LocalVarInfo>();
1264           }
1265           list.add(lv);
1266         }
1267       }
1268     }
1269     
1270     if (list == null){
1271       list = Collections.emptyList();
1272     }
1273     
1274     return list;
1275   }
1276   
1277   
1278   //--- line number table initialization
1279   // <2do> these are going away
1280   protected void startLineNumberTable(int lineNumberCount){
1281     int len = code.length;
1282     int[] ln = new int[len];
1283
1284     lineNumbers = ln;
1285   }
1286   protected void setLineNumber(int index, int lineNumber, int startPc){
1287     int len = code.length;
1288     int[] ln = lineNumbers;
1289
1290     for (int i=0; i<len; i++){
1291       Instruction insn = code[i];
1292       int pc = insn.getPosition();
1293
1294       if (pc == startPc){ // this is the first insn with this line number
1295         ln[i] = lineNumber;
1296         return;
1297       }
1298     }
1299   }
1300   protected void finishLineNumberTable (){
1301     int len = code.length;
1302     int[] ln = lineNumbers;
1303     int lastLine = ln[0];
1304
1305     for (int i=1; i<len; i++){
1306       if (ln[i] == 0){
1307         ln[i] = lastLine;
1308       } else {
1309         lastLine = ln[i];
1310       }
1311     }
1312   }
1313
1314   /**
1315    * note - this depends on that we already have a code array
1316    * and that the lines/startPcs are sorted (monotonic increasing)
1317    */
1318   public void setLineNumbers (int[] lines, int[] startPcs){
1319     int j=0;
1320     int lastLine = -1;
1321     
1322     int len = code.length;
1323     int[] ln = new int[len];
1324
1325     for (int i=0; i<len; i++){
1326       Instruction insn = code[i];
1327       int pc = insn.getPosition();
1328       
1329       if ((j < startPcs.length) && pc == startPcs[j]){
1330         lastLine = lines[j];
1331         j++;
1332       }
1333       
1334       ln[i] = lastLine;
1335     }
1336     
1337     lineNumbers = ln;
1338   }
1339
1340   /**
1341    * this version takes an already expanded line number array which has to be of
1342    * the same size as the code array
1343    */
1344   public void setLineNumbers (int[] lines){
1345     if (lines.length != code.length){
1346       throw new JPFException("inconsitent code/line number size");
1347     }
1348     lineNumbers = lines;
1349   }
1350   
1351   @Override
1352   public String toString() {
1353     return "MethodInfo[" + getFullName() + ']';
1354   }
1355   
1356   // for debugging purposes
1357   public void dump(){
1358     System.out.println("--- " + this);
1359     for (int i = 0; i < code.length; i++) {
1360       System.out.printf("%2d [%d]: %s\n", i, code[i].getPosition(), code[i].toString());
1361     }
1362   }
1363
1364   /**
1365    * Creates a method for a given class, by cloning this MethodInfo
1366    * and all the instructions belong to the method
1367    */
1368   public MethodInfo getInstanceFor(ClassInfo ci) {
1369     MethodInfo clone;
1370
1371     try {
1372       clone = (MethodInfo)super.clone();
1373       clone.ci = ci;
1374
1375       clone.globalId = mthTable.size();
1376       mthTable.add(this);
1377
1378       if(code == null) {
1379         clone.code = null;
1380       } else {
1381         clone.code = new Instruction[code.length];
1382
1383         for(int i=0; i<code.length; i++) {
1384           clone.code[i] = code[i].typeSafeClone(clone);
1385         }
1386       }
1387
1388     } catch (CloneNotSupportedException cnsx){
1389       cnsx.printStackTrace();
1390       return null;
1391     }
1392
1393     return clone;
1394   }
1395 }