001: //
002: // Copyright (C) 2005 United States Government as represented by the
003: // Administrator of the National Aeronautics and Space Administration
004: // (NASA). All Rights Reserved.
005: //
006: // This software is distributed under the NASA Open Source Agreement
007: // (NOSA), version 1.3. The NOSA has been approved by the Open Source
008: // Initiative. See the file NOSA-1.3-JPF at the top of the distribution
009: // directory tree for the complete NOSA document.
010: //
011: // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
012: // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
013: // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
014: // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
015: // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
016: // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
017: // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
018: //
019: package gov.nasa.jpf.jvm;
020:
021: import gov.nasa.jpf.JPFException;
022: import gov.nasa.jpf.jvm.bytecode.Instruction;
023: import gov.nasa.jpf.util.Debug;
024:
025: import org.apache.bcel.Constants;
026: import org.apache.bcel.classfile.Code;
027: import org.apache.bcel.classfile.CodeException;
028: import org.apache.bcel.classfile.ConstantPool;
029: import org.apache.bcel.classfile.LineNumberTable;
030: import org.apache.bcel.classfile.LocalVariable;
031: import org.apache.bcel.classfile.LocalVariableTable;
032: import org.apache.bcel.classfile.Method;
033: import org.apache.bcel.generic.InstructionHandle;
034: import org.apache.bcel.generic.InstructionList;
035: import gov.nasa.jpf.jvm.bytecode.RETURN;
036: import gov.nasa.jpf.jvm.bytecode.InvokeInstruction;
037: import gov.nasa.jpf.jvm.bytecode.INVOKESTATIC;
038: import gov.nasa.jpf.jvm.bytecode.INVOKESPECIAL;
039: import gov.nasa.jpf.jvm.bytecode.INVOKEVIRTUAL;
040:
041: /**
042: * information associated with a method. Each method in JPF
043: * is represented by a MethodInfo object
044: */
045: public class MethodInfo implements Cloneable {
046: /**
047: * Used to warn about local variable information.
048: */
049: protected static boolean warnedLocalInfo = false;
050: static final int MJI_NONE = 0;
051: static final int MJI_NATIVE = 0x1;
052: static final int MJI_NONDETERMINISTIC = 0x2;
053: static final int MJI_COND_DETERMINISTIC = 0x4;
054: static final int MJI_COND_EXECUTABLE = 0x8;
055:
056: /**
057: * scheduling relevance (used for on-the-fly POR)
058: */
059: public static final int SR_NEVER = 0; // never relevant, excl. sync blocks in body
060: public static final int SR_ALWAYS = 0x1; // always relevant
061: public static final int SR_RUNNABLES = 0x2; // only relevant if there are other runnables
062: public static final int SR_SYNC = 0x4; // only relevant if top lock level
063:
064: public static final int SR_NEVERBODY = 0x8; // never relevant, incl. sync blocks in body
065:
066: /**
067: * Name of the method.
068: */
069: protected String name;
070:
071: /**
072: * Signature of the method.
073: */
074: protected String signature;
075:
076: /**
077: * Class the method belongs to.
078: */
079: protected ClassInfo ci;
080:
081: /**
082: * Instructions associated with the method.
083: */
084: protected Instruction[] code;
085:
086: /**
087: * Exception handlers.
088: */
089: protected ExceptionHandler[] exceptions;
090:
091: /**
092: * Table used for line numbers.
093: */
094: protected int[] lineNumbers;
095:
096: /**
097: * Local variables names.
098: */
099: protected String[] localVariableNames;
100:
101: /**
102: * Local variables types.
103: */
104: protected String[] localVariableTypes;
105:
106: /**
107: * Static method.
108: */
109: protected boolean isStatic;
110:
111: /**
112: * Synchronized method.
113: */
114: protected boolean isSynchronized;
115:
116: /**
117: * that's our own attribute - execute this method atomically
118: */
119: protected boolean isAtomic;
120:
121: /**
122: * Native method.
123: */
124: protected boolean isNative;
125:
126: /**
127: * is this a scheduling relevant method (step boundary)
128: */
129: protected int schedulingRelevance;
130:
131: /**
132: * Maximum number of local variables.
133: */
134: protected int maxLocals;
135:
136: /**
137: * Maximum number of elements on the stack.
138: */
139: protected int maxStack;
140:
141: // <2do> pcm - turn this into a derived class, it's only required for MJI methods
142:
143: /**
144: * the number of stack slots for the arguments (incl. 'this'), lazy eval
145: */
146: private int argSize = -1;
147:
148: /**
149: * number of arguments (excl. 'this'), lazy eval
150: */
151: private int nArgs = -1;
152:
153: /**
154: * what return type do we have (again, lazy evaluated)
155: */
156: private byte returnType = -1;
157:
158: /**
159: * used for native method parameter conversion (lazy evaluated)
160: */
161: private byte[] argTypes = null;
162:
163: /**
164: * the various MJI method attrs (we don't want to burn 12 bytes for them
165: */
166: int mjiAttrs = MJI_NONE;
167:
168: /**
169: * this is a lazy evaluated mangled name consisting of the name and
170: * arg type signature
171: */
172: private String uniqueName;
173:
174: /**
175: * Creates a new method info.
176: */
177: protected MethodInfo(Method m, ClassInfo c) {
178: name = m.getName();
179: signature = m.getSignature();
180: ci = c;
181:
182: code = loadCode(m);
183: exceptions = loadExceptions(m);
184: lineNumbers = loadLineNumbers(m);
185: maxLocals = getMaxLocals(m);
186: maxStack = getMaxStack(m);
187: localVariableNames = loadLocalVariableNames(m);
188: localVariableTypes = loadLocalVariableTypes(m);
189: isStatic = m.isStatic();
190: isSynchronized = m.isSynchronized();
191: isNative = m.isNative();
192:
193: // since that's used to store the method in the ClassInfo, and to
194: // identify it in tne InvokeInstruction, we can set it here
195: uniqueName = getUniqueName(name, signature);
196: }
197:
198: void setAtomic(boolean isAtomic) {
199: this .isAtomic = isAtomic;
200: }
201:
202: public boolean isAtomic() {
203: return isAtomic;
204: }
205:
206: void setSchedulingRelevance(int sr) {
207: schedulingRelevance = sr;
208: }
209:
210: public Object clone() {
211: try {
212: return super .clone();
213: } catch (CloneNotSupportedException cnx) {
214: return null;
215: }
216: }
217:
218: /**
219: * NOTE - this only works in conjunction with a special StackFrame
220: */
221: MethodInfo createNativeCallStub() {
222: MethodInfo mi = (MethodInfo) clone();
223: String cname = ci.getName();
224: InvokeInstruction insn;
225:
226: if (isStatic) {
227: insn = new INVOKESTATIC(mi, cname, name, signature, 0, 0);
228: } else if (name.equals("<init>")) {
229: insn = new INVOKESPECIAL(mi, cname, name, signature, 0, 0);
230: } else {
231: insn = new INVOKEVIRTUAL(mi, cname, name, signature, 0, 0);
232: }
233:
234: mi.code = new Instruction[2];
235: mi.code[0] = insn;
236: mi.code[1] = new RETURN(mi, 1, 4);
237:
238: // otherwise ThreadInfo.executeAtomicLinesStep chokes up
239: mi.lineNumbers = null;
240: mi.exceptions = null;
241:
242: return mi;
243: }
244:
245: /**
246: * return the minimal name that has to be unique for overloading
247: * (name + argument part of signature)
248: * used as a lookup key
249: */
250: public static String getUniqueName(String mname, String signature) {
251: return (mname + signature.substring(0,
252: signature.indexOf(')') + 1));
253: }
254:
255: public static MethodInfo newInstance(Method m, ClassInfo c) {
256: return (new MethodInfo(m, c));
257: }
258:
259: public static MethodInfo[] newInstanceArray(int length) {
260: return (new MethodInfo[length]);
261: }
262:
263: public byte[] getArgumentTypes() {
264: if (argTypes == null) {
265: argTypes = Types.getArgumentTypes(signature);
266: }
267:
268: return argTypes;
269: }
270:
271: public int getArgumentsSize() {
272: if (argSize < 0) {
273: argSize = Types.getArgumentsSize(signature);
274:
275: if (!isStatic) {
276: argSize++;
277: }
278: }
279:
280: return argSize;
281: }
282:
283: /**
284: * Returns the class the method belongs to.
285: */
286: public ClassInfo getClassInfo() {
287: return ci;
288: }
289:
290: /**
291: * Return the complete name of the method, including the class name.
292: */
293: public String getCompleteName() {
294: return ci.getName() + "." + name + signature;
295: }
296:
297: public boolean isDeterministic(ThreadInfo ti) {
298: if ((mjiAttrs & MJI_COND_DETERMINISTIC) != 0) {
299: return ci.isMethodCondDeterministic(ti, this );
300: } else {
301: // really just makes sense if this is a MJI method
302: // but we will factor this into a subclass later-on anyway
303: return ((mjiAttrs & MJI_NONDETERMINISTIC) == 0);
304: }
305: }
306:
307: public boolean isExecutable(ThreadInfo ti) {
308: boolean canEnter = false;
309:
310: if ((mjiAttrs & MJI_COND_EXECUTABLE) != 0) {
311: canEnter = ci.isMethodCondExecutable(ti, this );
312: } else {
313: canEnter = canEnter(ti);
314: }
315:
316: return canEnter;
317: }
318:
319: /**
320: * do we honor scheduling relevant insns in our body, or do we
321: * (hopefully deliberately) ignore them
322: */
323: public boolean isBodySchedulingRelevant(ThreadInfo ti,
324: ElementInfo ei) {
325: return (schedulingRelevance != SR_NEVERBODY);
326: }
327:
328: /**
329: * is invocation of this method scheduling relevant
330: */
331: public boolean isSchedulingRelevant(ThreadInfo ti, ElementInfo ei) {
332: switch (schedulingRelevance) {
333: case SR_ALWAYS:
334: return true;
335: case SR_RUNNABLES:
336: return ti.hasOtherRunnables();
337: case SR_SYNC:
338: if (ei.getLockCount() == 0) {
339: return ti.hasOtherRunnables();
340: }
341: }
342:
343: return false;
344: }
345:
346: public boolean isCtor() {
347: return (name.equals("<init>"));
348: }
349:
350: public boolean isInternalMethod() {
351: // <2do> pcm - should turn this into an attribute for efficiency reasons
352: return (name.equals("<clinit>") || uniqueName
353: .equals("finalize()"));
354: }
355:
356: public boolean isThreadEntry(ThreadInfo ti) {
357: return (uniqueName.equals("run()") && (ti.countStackFrames() == 1));
358: }
359:
360: /**
361: * Returns the full name of the method, name and signature.
362: */
363: public String getFullName() {
364: return name + signature;
365: }
366:
367: /**
368: * Returns a specific instruction.
369: */
370: public Instruction getInstruction(int i) {
371: if (code == null) {
372: return null;
373: }
374:
375: if ((i < 0) || (i >= code.length)) {
376: return null;
377: }
378:
379: return code[i];
380: }
381:
382: /**
383: * Returns the instruction at a certain position.
384: */
385: public Instruction getInstructionAt(int position) {
386: if (code == null) {
387: return null;
388: }
389:
390: for (int i = 0, l = code.length; i < l; i++) {
391: if ((code[i] != null)
392: && (code[i].getPosition() == position)) {
393: return code[i];
394: }
395: }
396:
397: throw new JPFException("instruction not found");
398: }
399:
400: /**
401: * Returns the instructions of the method.
402: */
403: public Instruction[] getInstructions() {
404: return code;
405: }
406:
407: /**
408: * Returns the line number for a given position.
409: */
410: public int getLineNumber(Instruction pc) {
411: if (lineNumbers == null) {
412: return pc.getPosition();
413: }
414:
415: return lineNumbers[pc.getOffset()];
416: }
417:
418: /**
419: * Returns a table to translate positions into line numbers.
420: */
421: public int[] getLineNumbers() {
422: return lineNumbers;
423: }
424:
425: public boolean isMJI() {
426: return ((mjiAttrs & MJI_NATIVE) != 0);
427: }
428:
429: public int getMaxLocals() {
430: return maxLocals;
431: }
432:
433: public static int getMaxLocals(Method m) {
434: Code c = m.getCode();
435:
436: if (c == null) {
437: return 0;
438: }
439:
440: return c.getMaxLocals();
441: }
442:
443: public int getMaxStack() {
444: return maxStack;
445: }
446:
447: public static int getMaxStack(Method m) {
448: Code c = m.getCode();
449:
450: if (c == null) {
451: return 0;
452: }
453:
454: return c.getMaxStack();
455: }
456:
457: public ExceptionHandler[] getExceptions() {
458: return exceptions;
459: }
460:
461: public String[] getLocalVariableNames() {
462: return localVariableNames;
463: }
464:
465: public String[] getLocalVariableTypes() {
466: return localVariableTypes;
467: }
468:
469: /**
470: * Returns the name of the method.
471: */
472: public String getName() {
473: return name;
474: }
475:
476: /**
477: * Returns true if the method is native
478: */
479: public boolean isNative() {
480: return isNative;
481: }
482:
483: public int getNumberOfArguments() {
484: if (nArgs < 0) {
485: nArgs = Types.getNumberOfArguments(signature);
486: }
487:
488: return nArgs;
489: }
490:
491: /**
492: * Returns the size of the arguments.
493: * This returns the number of parameters passed on the stack, incl. 'this'
494: */
495: public int getNumberOfStackArguments() {
496: int n = getNumberOfArguments();
497:
498: return isStatic ? n : n + 1;
499: }
500:
501: /**
502: * do we return Object references?
503: */
504: public boolean isReferenceReturnType() {
505: int r = getReturnType();
506:
507: return ((r == Types.T_REFERENCE) || (r == Types.T_ARRAY));
508: }
509:
510: public byte getReturnType() {
511: if (returnType < 0) {
512: returnType = Types.getReturnType(signature);
513: }
514:
515: return returnType;
516: }
517:
518: /**
519: * Returns the signature of the method.
520: */
521: public String getSignature() {
522: return signature;
523: }
524:
525: /**
526: * Returns true if the field is static.
527: */
528: public boolean isStatic() {
529: return isStatic;
530: }
531:
532: /**
533: * Returns true if the field is synchronized.
534: */
535: public boolean isSynchronized() {
536: return isSynchronized;
537: }
538:
539: public String getUniqueName() {
540: return uniqueName;
541: }
542:
543: public boolean canEnter(ThreadInfo th) {
544: if (isSynchronized) {
545: ElementInfo ei = getBlockedObject(th, true);
546:
547: // <?> pcm - the other way round would be intuitive
548: return ei.canLock(th);
549: }
550:
551: return true;
552: }
553:
554: public ElementInfo getBlockedObject(ThreadInfo th,
555: boolean isBeforeCall) {
556: int objref;
557: ElementInfo ei = null;
558:
559: if (isSynchronized) {
560: if (isStatic) {
561: objref = ci.getClassObjectRef();
562: } else {
563: // NOTE 'inMethod' doesn't work for natives, because th.getThis()
564: // pulls 'this' from the stack frame, which we don't have (and don't need)
565: // for natives
566: objref = isBeforeCall ? th.getCalleeThis(this ) : th
567: .getThis();
568: }
569:
570: DynamicArea da = JVM.getVM().getDynamicArea();
571: ei = da.get(objref);
572:
573: if (ei == null) {
574: // ARGHH, broken this or class
575: throw new JPFException(
576: "inconsistent stack, no object or class ref: "
577: + getCompleteName() + " (" + objref
578: + ")");
579: }
580: }
581:
582: return ei;
583: }
584:
585: public void enter(ThreadInfo th) {
586: if (isSynchronized) {
587: ElementInfo ei = getBlockedObject(th, false);
588: ei.lock(th);
589: }
590: }
591:
592: public void leave(ThreadInfo th) {
593: if (isSynchronized) {
594: ElementInfo ei = getBlockedObject(th, false);
595: ei.unlock(th);
596: }
597: }
598:
599: /**
600: * execute this method, which might be either bytecode or native.
601: */
602: public Instruction execute(ThreadInfo ti, boolean isDirectCall) {
603: if (((mjiAttrs & MJI_NATIVE) != 0) || isNative) {
604: // if we have a native method which is not MJI_NATIVE, we are in
605: // trouble (ends up in UnsatisfiedLinkErrors)
606: // the opposite (MJI_NATIVE but not native) is questionable, but legal
607: // (you better know what code you cut off)
608: return ci.executeNativeMethod(ti, this );
609: } else {
610: ti.pushFrame(new StackFrame(this , isDirectCall, ti.top()));
611: enter(ti);
612:
613: return ti.getPC();
614: }
615: }
616:
617: /**
618: * Loads the code of the method.
619: */
620: protected Instruction[] loadCode(Method m) {
621: Code c = m.getCode();
622:
623: if (c == null) {
624: return null;
625: }
626:
627: InstructionList il = new InstructionList(c.getCode());
628:
629: InstructionHandle[] hs = il.getInstructionHandles();
630: int length = hs.length;
631:
632: Instruction[] is = new Instruction[length];
633:
634: for (int i = 0; i < length; i++) {
635: is[i] = Instruction.create(hs[i], i, this , m
636: .getConstantPool());
637:
638: if (c.getLineNumberTable() != null) {
639: // annoying bug when BCEL don't seem to find linenumber - pos match
640: // also sometimes linenumber tables are not available
641: is[i].setContext(ci.getName(), name, c
642: .getLineNumberTable().getSourceLine(
643: is[i].getPosition()), is[i]
644: .getPosition());
645: }
646: }
647:
648: return is;
649: }
650:
651: /**
652: * Returns the exceptions of the method.
653: */
654: protected ExceptionHandler[] loadExceptions(Method m) {
655: Code c = m.getCode();
656:
657: if (c == null) {
658: return null;
659: }
660:
661: CodeException[] ce = c.getExceptionTable();
662:
663: if (ce.length == 0) {
664: return null;
665: }
666:
667: int length = ce.length;
668: ExceptionHandler[] eh = new ExceptionHandler[length];
669:
670: ConstantPool cp = m.getConstantPool();
671:
672: for (int i = 0; i < length; i++) {
673: int ct = ce[i].getCatchType();
674: eh[i] = new ExceptionHandler(((ct == 0) ? null : cp
675: .getConstantString(ct, Constants.CONSTANT_Class)
676: .replace('/', '.')), ce[i].getStartPC(), ce[i]
677: .getEndPC(), ce[i].getHandlerPC());
678: }
679:
680: return eh;
681: }
682:
683: /**
684: * Loads the line numbers for the method.
685: */
686: protected int[] loadLineNumbers(Method m) {
687: Code c = m.getCode();
688:
689: if (c == null) {
690: return null;
691: }
692:
693: LineNumberTable lnt = c.getLineNumberTable();
694:
695: int length = code.length;
696: int[] ln = new int[length];
697:
698: if (lnt == null) {
699: // no line information
700: return null;
701: } else {
702: for (int i = 0; i < length; i++) {
703: try { //annoying bug when BCEL don't seem to find linenumber - pos match
704: ln[i] = lnt.getSourceLine(code[i].getPosition());
705: } catch (RuntimeException e) {
706: System.out.print("^");
707: }
708: }
709: }
710:
711: return ln;
712: }
713:
714: /**
715: * Loads the names of the local variables.
716: *
717: * NOTE: BCEL only gives us a list of all *named* locals, which might not
718: * include all local vars (temporaries, like StringBuffer). Note that we have
719: * to fill this with "?" inorder to make the returned array correspond with
720: * slot numbers
721: */
722: protected String[] loadLocalVariableNames(Method m) {
723: Code c = m.getCode();
724:
725: if (c == null) {
726: return null;
727: }
728:
729: LocalVariableTable lvt = c.getLocalVariableTable();
730:
731: if (lvt == null) {
732: if (!warnedLocalInfo && !ci.isSystemClass()) {
733: Debug.println(Debug.WARNING);
734: Debug.println(Debug.WARNING,
735: "No local variable information available");
736: Debug
737: .println(Debug.WARNING, "for "
738: + getCompleteName());
739: Debug
740: .println(Debug.WARNING,
741: "Recompile with -g to include this information");
742: Debug.println(Debug.WARNING);
743: warnedLocalInfo = true;
744: }
745:
746: return null;
747: }
748:
749: LocalVariable[] lv = lvt.getLocalVariableTable();
750: int length = lv.length;
751: String[] v = new String[c.getMaxLocals()];
752:
753: for (int i = 0; i < length; i++) {
754: v[lv[i].getIndex()] = lv[i].getName();
755: }
756:
757: for (int i = 0; i < v.length; i++) {
758: if (v[i] == null) {
759: v[i] = "?";
760: }
761: }
762:
763: return v;
764: }
765:
766: /**
767: * Loads the types of the local variables.
768: * see loadLocalVariableNames for the problem with temporaries and
769: * why we can't copy the types 1:1
770: */
771: protected String[] loadLocalVariableTypes(Method m) {
772: Code c = m.getCode();
773:
774: if (c == null) {
775: return null;
776: }
777:
778: LocalVariableTable lvt = c.getLocalVariableTable();
779:
780: if (lvt == null) {
781: if (!warnedLocalInfo && !ci.isSystemClass()) {
782: Debug.println(Debug.WARNING,
783: "No local variable information available");
784: Debug
785: .println(Debug.WARNING, "for "
786: + getCompleteName());
787: Debug
788: .println(Debug.WARNING,
789: "Recompile with -g to include this information");
790: Debug.println(Debug.WARNING);
791: warnedLocalInfo = true;
792: }
793:
794: return null;
795: }
796:
797: LocalVariable[] lv = lvt.getLocalVariableTable();
798: int length = lv.length;
799: String[] v = new String[c.getMaxLocals()];
800:
801: for (int i = 0; i < length; i++) {
802: v[lv[i].getIndex()] = lv[i].getSignature();
803: }
804:
805: for (int i = 0; i < v.length; i++) {
806: if (v[i] == null) {
807: v[i] = "?";
808: }
809: }
810:
811: return v;
812: }
813:
814: void setCondDeterministic(boolean isCondDeterministic) {
815: if (isCondDeterministic) {
816: mjiAttrs |= MJI_COND_DETERMINISTIC;
817: } else {
818: mjiAttrs &= ~MJI_COND_DETERMINISTIC;
819: }
820: }
821:
822: void setCondExecutable(boolean isCondExecutable) {
823: if (isCondExecutable) {
824: mjiAttrs |= MJI_COND_EXECUTABLE;
825: } else {
826: mjiAttrs &= ~MJI_COND_EXECUTABLE;
827: }
828: }
829:
830: void setDeterministic(boolean isDeterministic) {
831: if (isDeterministic) {
832: mjiAttrs &= ~MJI_NONDETERMINISTIC;
833: } else {
834: mjiAttrs |= MJI_NONDETERMINISTIC;
835: }
836: }
837:
838: void setMJI(boolean isMJI) {
839: if (isMJI) {
840: mjiAttrs |= MJI_NATIVE;
841: } else {
842: mjiAttrs &= ~MJI_NATIVE;
843: }
844: }
845: }
|