Fixing a bug: There is a non-generic class with genericSignature present inside.
[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     if (!genericSignature.contains(":"))
513       return new String[0];
514     return Types.getArgumentTypeNames(genericSignature);
515   }
516
517   public int getArgumentsSize () {
518     if (argSize < 0) {
519       argSize = Types.getArgumentsSize(signature);
520
521       if (!isStatic()) {
522         argSize++;
523       }
524     }
525
526     return argSize;
527   }
528   
529   /**
530    * return only the LocalVarInfos for arguments, in order of definition
531    * or null if there are no localVarInfos.
532    * throw a JPFException if there are more immediately in scope vars than args
533    * 
534    * NOTE - it is perfectly legal for a method to have arguments but no LocalVarInfos,
535    * which are code attributes, clients have to check for a non-null return value
536    * even if the method has arguments.
537    * Note also that abstract / interface methods don't have code and hence no
538    * LocalVarInfos
539    */
540   public LocalVarInfo[] getArgumentLocalVars(){
541     if (localVars == null){ // shortcut in case we don't have args or localVars;
542       return null;
543     }
544     
545     int nArgs = getNumberOfStackArguments(); // we want 'this'
546     if (nArgs == 0){
547       return new LocalVarInfo[0]; // rare enough so that we don't use a static
548     }
549
550     LocalVarInfo[] argLvis = new LocalVarInfo[nArgs];
551     int n = 0; // how many args we've got so far
552     
553     for (LocalVarInfo lvi : localVars){
554       // arguments are the only ones that are immediately in scope
555       if (lvi.getStartPC() == 0){
556         if (n == nArgs){ // ARGH - more in-scope vars than args
557           throw new JPFException("inconsistent localVar table for method " + getFullName());
558         }
559         
560         // order with respect to slot index - since this might get called
561         // frequently, we don't use java.util.Arrays.sort() but sort in
562         // on-the-fly. Note that we can have several localVar entries for the
563         // same name, but only one can be immediately in scope
564         int slotIdx = lvi.getSlotIndex();
565
566         int i;
567         for (i = 0; i < n; i++) {
568           if (slotIdx < argLvis[i].getSlotIndex()) {
569             for (int j=n; j>i; j--){
570               argLvis[j] = argLvis[j-1];
571             }
572             argLvis[i] = lvi;
573             n++;
574             break;
575           }
576         }
577         if (i == n) { // append
578           argLvis[n++] = lvi;
579         }
580       }
581     }
582     
583     return argLvis;
584   }
585   
586   public String getReturnType () {
587     return Types.getReturnTypeSignature(signature);
588   }
589
590   public String getReturnTypeName () {
591     return Types.getReturnTypeName(signature);
592   }
593
594   public String getGenericReturnTypeName () {
595     // TODO: We need to double check but for some reason Groovy has a type of generic signature with "<*>"
596     // TODO: in the class file.
597     if (genericSignature == null || genericSignature.equals("") || genericSignature.contains("<*>"))
598       return Types.getReturnTypeName(signature);
599     if (!genericSignature.contains(":"))
600       return Types.getReturnTypeName(signature);
601     return Types.getGenericReturnTypeName(genericSignature);
602   }
603
604   public String getSourceFileName () {
605     if (ci != null) {
606       return ci.getSourceFileName();
607     } else {
608       return "[VM]";
609     }
610   }
611
612   public String getClassName () {
613     if (ci != null) {
614       return ci.getName();
615     } else {
616       return "[VM]";
617     }
618   }
619   
620   /**
621    * Returns the class the method belongs to.
622    */
623   public ClassInfo getClassInfo () {
624     return ci;
625   }
626
627   /**
628    * @deprecated - use getFullName
629    */
630   @Deprecated
631 public String getCompleteName () {
632     return getFullName();
633   }
634
635   /**
636    * return classname.name (but w/o signature)
637    */
638   public String getBaseName() {
639     return getClassName() + '.' + name;
640   }
641     
642   public boolean isCtor () {
643     return (name.equals("<init>"));
644   }
645   
646   public boolean isInternalMethod () {
647     // <2do> pcm - should turn this into an attribute for efficiency reasons
648     return (name.equals("<clinit>") || uniqueName.equals("finalize()V"));
649   }
650   
651   public boolean isThreadEntry (ThreadInfo ti) {
652     return (uniqueName.equals("run()V") && (ti.countStackFrames() == 1));
653   }
654   
655   /**
656    * Returns the full classname (if any) + name + signature.
657    */
658   public String getFullName () {
659     if (ci != null) {
660       return ci.getName() + '.' + getUniqueName();
661     } else {
662       return getUniqueName();
663     }
664   }
665
666   /**
667    * returns stack trace name: classname (if any) + name
668    */
669   public String getStackTraceName(){
670     if (ci != null) {
671       return ci.getName() + '.' + name;
672     } else {
673       return name;
674     }
675   }
676   
677   /**
678    * return number of instructions
679    */
680   public int getNumberOfInstructions() {
681     if (code == null){
682       return 0;
683     }
684     
685     return code.length;
686   }
687   
688   /**
689    * Returns a specific instruction.
690    */
691   public Instruction getInstruction (int i) {
692     if (code == null) {
693       return null;
694     }
695
696     if ((i < 0) || (i >= code.length)) {
697       return null;
698     }
699
700     return code[i];
701   }
702
703   /**
704    * Returns the instruction at a certain position.
705    */
706   public Instruction getInstructionAt (int position) {
707     if (code == null) {
708       return null;
709     }
710
711     for (int i = 0, l = code.length; i < l; i++) {
712       if ((code[i] != null) && (code[i].getPosition() == position)) {
713         return code[i];
714       }
715     }
716
717     throw new JPFException("instruction not found");
718   }
719
720   /**
721    * Returns the instructions of the method.
722    */
723   public Instruction[] getInstructions () {
724     return code;
725   }
726   
727   public boolean includesLine (int line){
728     int len = code.length;
729     return (code[0].getLineNumber() <= line) && (code[len].getLineNumber() >= line);
730   }
731
732   public Instruction[] getInstructionsForLine (int line){
733     return getInstructionsForLineInterval(line,line);
734   }
735
736   public Instruction[] getInstructionsForLineInterval (int l1, int l2){
737     Instruction[] c = code;
738        
739     // instruction line numbers don't have to be monotonic (they can decrease for loops)
740     // hence we cannot easily check for overlapping ranges
741     
742     if (c != null){
743        ArrayList<Instruction> matchingInsns = null;
744        
745        for (int i = 0; i < c.length; i++) {
746         Instruction insn = c[i];
747         int line = insn.getLineNumber();
748         if (line == l1 || line == l2 || (line > l1 && line < l2)) {
749           if (matchingInsns == null) {
750             matchingInsns = new ArrayList<Instruction>();
751           }
752           matchingInsns.add(insn);
753         }
754       }
755       
756       if (matchingInsns == null) {
757         return null;
758       } else {
759         return matchingInsns.toArray(new Instruction[matchingInsns.size()]);
760       }
761             
762     } else {
763       return null;
764     }
765   }
766
767   public Instruction[] getMatchingInstructions (LocationSpec lspec){
768     return getInstructionsForLineInterval(lspec.getFromLine(), lspec.getToLine());
769   }
770
771
772   /**
773    * Returns the line number for a given position.
774    */
775   public int getLineNumber (Instruction pc) {
776     if (lineNumbers == null) {
777       if (pc == null)
778         return -1;
779       else
780         return pc.getPosition();
781     }
782
783     if (pc != null) {
784       int idx = pc.getInstructionIndex();
785       if (idx < 0) idx = 0;
786       return lineNumbers[idx];
787     } else {
788       return -1;
789     }
790   }
791
792   /**
793    * Returns a table to translate positions into line numbers.
794    */
795   public int[] getLineNumbers () {
796     return lineNumbers;
797   }
798
799   public boolean containsLineNumber (int n){
800     if (lineNumbers != null){
801       return (lineNumbers[0] <= n) && (lineNumbers[lineNumbers.length-1] <= n);
802     }
803     
804     return false;
805   }
806   
807   public boolean intersectsLineNumbers( int first, int last){
808     if (lineNumbers != null){
809       if ((last < lineNumbers[0]) || (first > lineNumbers[lineNumbers.length-1])){
810         return false;
811       }
812       return true;
813     }
814     
815     return false;
816   }
817   
818   public ExceptionHandler getHandlerFor (ClassInfo ciException, Instruction insn){
819     if (exceptionHandlers != null){
820       int position = insn.getPosition();
821       for (int i=0; i<exceptionHandlers.length; i++){
822         ExceptionHandler handler = exceptionHandlers[i];
823         if ((position >= handler.getBegin()) && (position < handler.getEnd())) {
824           // checks if this type of exception is caught here (null means 'any')
825           String handledType = handler.getName();
826           if ((handledType == null)   // a catch-all handler
827                   || ciException.isInstanceOf(handledType)) {
828             return handler;
829           }
830         }          
831       }      
832     }
833     
834     return null;
835   }
836   
837   public boolean isMJI () {
838     return false;
839   }
840
841   public int getMaxLocals () {
842     return maxLocals;
843   }
844
845   public int getMaxStack () {
846     return maxStack;
847   }
848
849   public ExceptionHandler[] getExceptions () {
850     return exceptionHandlers;
851   }
852
853   public String[] getThrownExceptionClassNames () {
854     return thrownExceptionClassNames;
855   }
856
857
858   public LocalVarInfo getLocalVar(String name, int pc){
859     LocalVarInfo[] vars = localVars;
860     if (vars != null){
861       for (int i = 0; i < vars.length; i++) {
862         LocalVarInfo lv = vars[i];
863         if (lv.matches(name, pc)) {
864           return lv;
865         }
866       }
867     }
868
869     return null;
870
871   }
872
873   public LocalVarInfo getLocalVar (int slotIdx, int pc){
874     LocalVarInfo[] vars = localVars;
875
876     if (vars != null){
877       for (int i = 0; i < vars.length; i++) {
878         LocalVarInfo lv = vars[i];
879         if (lv.matches(slotIdx, pc)) {
880           return lv;
881         }
882       }
883     }
884
885     return null;
886   }
887
888   public LocalVarInfo[] getLocalVars() {
889     return localVars; 
890   }
891
892
893   /**
894    * note that this might contain duplicates for variables with multiple
895    * scope entries
896    */
897   public String[] getLocalVariableNames() {
898     String[] names = new String[localVars.length];
899
900     for (int i=0; i<localVars.length; i++){
901       names[i] = localVars[i].getName();
902     }
903
904     return names;
905   }
906
907
908   public MethodInfo getOverriddenMethodInfo(){
909     MethodInfo smi = null;
910     
911     if (ci != null) {
912       ClassInfo sci = ci.getSuperClass();
913       if (sci != null){
914         smi = sci.getMethod(getUniqueName(), true);
915       }
916     }
917     
918     return smi;
919   }
920   
921   /**
922    * Returns the name of the method.
923    */
924   public String getName () {
925     return name;
926   }
927
928   public String getJNIName () {
929     return Types.getJNIMangledMethodName(null, name, signature);
930   }
931   
932   public int getModifiers () {
933     return modifiers;
934   }
935   
936   /**
937    * Returns true if the method is native
938    */
939   public boolean isNative () {
940     return ((modifiers & Modifier.NATIVE) != 0);
941   }
942
943   public boolean isAbstract () {
944     return ((modifiers & Modifier.ABSTRACT) != 0);
945   }
946   
947   // overridden by NativeMethodInfo
948   public boolean isUnresolvedNativeMethod(){
949     return ((modifiers & Modifier.NATIVE) != 0);
950   }
951
952   // overridden by NativeMethodInfo
953   public boolean isJPFExecutable (){
954     return !hasAttr(NoJPFExec.class);
955   }
956
957   public int getNumberOfArguments () {
958     if (nArgs < 0) {
959       nArgs = Types.getNumberOfArguments(signature);
960     }
961
962     return nArgs;
963   }
964
965   /**
966    * Returns the size of the arguments.
967    * This returns the number of parameters passed on the stack, incl. 'this'
968    */
969   public int getNumberOfStackArguments () {
970     int n = getNumberOfArguments();
971
972     return isStatic() ? n : n + 1;
973   }
974
975   public int getNumberOfCallerStackSlots () {
976     return Types.getNumberOfStackSlots(signature, isStatic()); // includes return type
977   }
978
979   public Instruction getFirstInsn(){
980     if (code != null){
981       return code[0];
982     }
983     return null;    
984   }
985   
986   public Instruction getLastInsn() {
987     if (code != null){
988       return code[code.length-1];
989     }
990     return null;
991   }
992
993   /**
994    * do we return Object references?
995    */
996   public boolean isReferenceReturnType () {
997     int r = getReturnTypeCode();
998
999     return ((r == Types.T_REFERENCE) || (r == Types.T_ARRAY));
1000   }
1001
1002   public byte getReturnTypeCode () {
1003     if (returnType < 0) {
1004       returnType = Types.getReturnBuiltinType(signature);
1005     }
1006
1007     return returnType;
1008   }
1009
1010   /**
1011    * what is the slot size of the return value
1012    */
1013   public int getReturnSize() {
1014     if (retSize == -1){
1015       switch (getReturnTypeCode()) {
1016         case Types.T_VOID:
1017           retSize = 0;
1018           break;
1019
1020         case Types.T_LONG:
1021         case Types.T_DOUBLE:
1022           retSize = 2;
1023           break;
1024
1025         default:
1026           retSize = 1;
1027           break;
1028       }
1029     }
1030
1031     return retSize;
1032   }
1033
1034   public Class<? extends ChoiceGenerator<?>> getReturnChoiceGeneratorType (){
1035     switch (getReturnTypeCode()){
1036       case Types.T_BOOLEAN:
1037         return BooleanChoiceGenerator.class;
1038
1039       case Types.T_BYTE:
1040       case Types.T_CHAR:
1041       case Types.T_SHORT:
1042       case Types.T_INT:
1043         return IntChoiceGenerator.class;
1044
1045       case Types.T_LONG:
1046         return LongChoiceGenerator.class;
1047
1048       case Types.T_FLOAT:
1049         return FloatChoiceGenerator.class;
1050
1051       case Types.T_DOUBLE:
1052         return DoubleChoiceGenerator.class;
1053
1054       case Types.T_ARRAY:
1055       case Types.T_REFERENCE:
1056       case Types.T_VOID:
1057         return ReferenceChoiceGenerator.class;
1058     }
1059
1060     return null;
1061   }
1062
1063   /**
1064    * Returns the signature of the method.
1065    */
1066   public String getSignature () {
1067     return signature;
1068   }
1069
1070   @Override
1071   public String getGenericSignature() {
1072     return genericSignature;
1073   }
1074
1075   @Override
1076   public void setGenericSignature(String sig){
1077     genericSignature = sig;
1078   }
1079
1080   /**
1081    * Returns true if the method is static.
1082    */
1083   public boolean isStatic () {
1084     return ((modifiers & Modifier.STATIC) != 0);
1085   }
1086
1087   /**
1088    * is this a public method
1089    */
1090   public boolean isPublic() {
1091     return ((modifiers & Modifier.PUBLIC) != 0);
1092   }
1093   
1094   public boolean isPrivate() {
1095     return ((modifiers & Modifier.PRIVATE) != 0);
1096   }
1097   
1098   public boolean isProtected() {
1099     return ((modifiers & Modifier.PROTECTED) != 0);
1100   }
1101
1102   /**
1103    * Returns true if the method is synchronized.
1104    */
1105   public boolean isSynchronized () {
1106     return ((modifiers & Modifier.SYNCHRONIZED) != 0);
1107   }
1108
1109   // <2do> these modifiers are still java.lang.reflect internal and not
1110   // supported by public Modifier methods, but since we want to keep this 
1111   // similar to the Method reflection and we get the modifiers from the
1112   // classfile we implement this with explicit values
1113   
1114   public boolean isSynthetic(){
1115     return ((modifiers & 0x00001000) != 0);    
1116   } 
1117   public boolean isVarargs(){
1118     return ((modifiers & 0x00000080) != 0);        
1119   }
1120   
1121   /*
1122    * is this from a classfile or was it created by JPF (and hence should not
1123    * be visible in stacktraces etc)
1124    */
1125   public boolean isJPFInternal(){
1126     // note this has a different meaning than Method.isSynthetic(), which
1127     // is defined in VM spec 4.7.8. What we mean here is that this MethodInfo
1128     // is not associated with any class (such as direct call MethodInfos), but
1129     // there might be more in the future
1130     return (ci == null);
1131   }
1132   
1133   public String getUniqueName () {
1134     return uniqueName;
1135   }
1136   
1137   public boolean hasCode(){
1138     return (code != null);
1139   }
1140   
1141   public boolean hasEmptyBody (){
1142     // only instruction is a return
1143     return (code.length == 1 && (code[0] instanceof ReturnInstruction));
1144   }
1145
1146
1147   //--- parameter annotations
1148   //<2do> these are going away
1149   protected void startParameterAnnotations(int annotationCount){
1150     parameterAnnotations = new AnnotationInfo[annotationCount][];
1151   }
1152   protected void setParameterAnnotations(int index, AnnotationInfo[] ai){
1153     parameterAnnotations[index] = ai;
1154   }
1155   protected void finishParameterAnnotations(){
1156     // nothing
1157   }
1158
1159   public void setParameterAnnotations (AnnotationInfo[][] parameterAnnotations){
1160     this.parameterAnnotations = parameterAnnotations;
1161   }
1162   
1163   //--- thrown exceptions
1164   //<2do> these are going away
1165   protected void startTrownExceptions (int exceptionCount){
1166     thrownExceptionClassNames = new String[exceptionCount];
1167   }
1168   protected void setException (int index, String exceptionType){
1169     thrownExceptionClassNames[index] = Types.getClassNameFromTypeName(exceptionType);
1170   }
1171   protected void finishThrownExceptions(){
1172     // nothing
1173   }
1174
1175   public void setThrownExceptions (String[] exceptions){
1176     thrownExceptionClassNames = exceptions;
1177   }
1178   
1179
1180   //--- exception handler table initialization
1181   //<2do> these are going away
1182   protected void startExceptionHandlerTable (int handlerCount){
1183     exceptionHandlers = new ExceptionHandler[handlerCount];
1184   }
1185   protected void setExceptionHandler (int index, int startPc, int endPc, int handlerPc, String catchType){
1186     exceptionHandlers[index] = new ExceptionHandler(catchType, startPc, endPc, handlerPc);
1187   }
1188   protected void finishExceptionHandlerTable(){
1189     // nothing
1190   }
1191
1192   public void setExceptionHandlers (ExceptionHandler[] handlers){
1193     exceptionHandlers = handlers;
1194   }
1195   
1196   //--- local var table initialization
1197   // <2do> these are going away
1198   protected void startLocalVarTable (int localVarCount){
1199     localVars = new LocalVarInfo[localVarCount];
1200   }
1201   protected void setLocalVar(int index, String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex){
1202     localVars[index] = new LocalVarInfo(varName, descriptor, "", scopeStartPc, scopeEndPc, slotIndex);
1203   }
1204   protected void finishLocalVarTable(){
1205     // nothing to do
1206   }
1207
1208   public void setLocalVarTable (LocalVarInfo[] locals){
1209     localVars = locals;
1210   }
1211   
1212   public void setLocalVarAnnotations (){
1213     if (localVars != null){
1214       for (VariableAnnotationInfo ai : getTargetTypeAnnotations(VariableAnnotationInfo.class)){
1215         for (int i = 0; i < ai.getNumberOfScopeEntries(); i++) {
1216           for (LocalVarInfo lv : localVars) {
1217             if (lv.getStartPC() == ai.getStartPC(i) && lv.getSlotIndex() == ai.getSlotIndex(i)) {
1218               lv.addTypeAnnotation(ai);
1219             }
1220           }
1221         }
1222       }
1223     }
1224   }
1225   
1226   public boolean hasTypeAnnotatedLocalVars (){
1227     if (localVars != null){
1228       for (LocalVarInfo lv : localVars){
1229         if (lv.hasTypeAnnotations()){
1230           return true;
1231         }
1232       }
1233     }
1234     
1235     return false;
1236   }
1237   
1238   public List<LocalVarInfo> getTypeAnnotatedLocalVars (){
1239     List<LocalVarInfo> list = null;
1240     
1241     if (localVars != null){
1242       for (LocalVarInfo lv : localVars){
1243         if (lv.hasTypeAnnotations()){
1244           if (list == null){
1245             list = new ArrayList<LocalVarInfo>();
1246           }
1247           list.add(lv);
1248         }
1249       }
1250     }
1251     
1252     if (list == null){
1253       list = Collections.emptyList();
1254     }
1255     
1256     return list;
1257   }
1258   
1259   public List<LocalVarInfo> getTypeAnnotatedLocalVars (String annotationClsName){
1260     List<LocalVarInfo> list = null;
1261     
1262     if (localVars != null){
1263       for (LocalVarInfo lv : localVars){
1264         AbstractTypeAnnotationInfo tai = lv.getTypeAnnotation(annotationClsName);
1265         if (tai != null){
1266           if (list == null){
1267             list = new ArrayList<LocalVarInfo>();
1268           }
1269           list.add(lv);
1270         }
1271       }
1272     }
1273     
1274     if (list == null){
1275       list = Collections.emptyList();
1276     }
1277     
1278     return list;
1279   }
1280   
1281   
1282   //--- line number table initialization
1283   // <2do> these are going away
1284   protected void startLineNumberTable(int lineNumberCount){
1285     int len = code.length;
1286     int[] ln = new int[len];
1287
1288     lineNumbers = ln;
1289   }
1290   protected void setLineNumber(int index, int lineNumber, int startPc){
1291     int len = code.length;
1292     int[] ln = lineNumbers;
1293
1294     for (int i=0; i<len; i++){
1295       Instruction insn = code[i];
1296       int pc = insn.getPosition();
1297
1298       if (pc == startPc){ // this is the first insn with this line number
1299         ln[i] = lineNumber;
1300         return;
1301       }
1302     }
1303   }
1304   protected void finishLineNumberTable (){
1305     int len = code.length;
1306     int[] ln = lineNumbers;
1307     int lastLine = ln[0];
1308
1309     for (int i=1; i<len; i++){
1310       if (ln[i] == 0){
1311         ln[i] = lastLine;
1312       } else {
1313         lastLine = ln[i];
1314       }
1315     }
1316   }
1317
1318   /**
1319    * note - this depends on that we already have a code array
1320    * and that the lines/startPcs are sorted (monotonic increasing)
1321    */
1322   public void setLineNumbers (int[] lines, int[] startPcs){
1323     int j=0;
1324     int lastLine = -1;
1325     
1326     int len = code.length;
1327     int[] ln = new int[len];
1328
1329     for (int i=0; i<len; i++){
1330       Instruction insn = code[i];
1331       int pc = insn.getPosition();
1332       
1333       if ((j < startPcs.length) && pc == startPcs[j]){
1334         lastLine = lines[j];
1335         j++;
1336       }
1337       
1338       ln[i] = lastLine;
1339     }
1340     
1341     lineNumbers = ln;
1342   }
1343
1344   /**
1345    * this version takes an already expanded line number array which has to be of
1346    * the same size as the code array
1347    */
1348   public void setLineNumbers (int[] lines){
1349     if (lines.length != code.length){
1350       throw new JPFException("inconsitent code/line number size");
1351     }
1352     lineNumbers = lines;
1353   }
1354   
1355   @Override
1356   public String toString() {
1357     return "MethodInfo[" + getFullName() + ']';
1358   }
1359   
1360   // for debugging purposes
1361   public void dump(){
1362     System.out.println("--- " + this);
1363     for (int i = 0; i < code.length; i++) {
1364       System.out.printf("%2d [%d]: %s\n", i, code[i].getPosition(), code[i].toString());
1365     }
1366   }
1367
1368   /**
1369    * Creates a method for a given class, by cloning this MethodInfo
1370    * and all the instructions belong to the method
1371    */
1372   public MethodInfo getInstanceFor(ClassInfo ci) {
1373     MethodInfo clone;
1374
1375     try {
1376       clone = (MethodInfo)super.clone();
1377       clone.ci = ci;
1378
1379       clone.globalId = mthTable.size();
1380       mthTable.add(this);
1381
1382       if(code == null) {
1383         clone.code = null;
1384       } else {
1385         clone.code = new Instruction[code.length];
1386
1387         for(int i=0; i<code.length; i++) {
1388           clone.code[i] = code[i].typeSafeClone(clone);
1389         }
1390       }
1391
1392     } catch (CloneNotSupportedException cnsx){
1393       cnsx.printStackTrace();
1394       return null;
1395     }
1396
1397     return clone;
1398   }
1399 }