View Javadoc
1   /*
2    * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   *
23   */
24  
25  package sun.jvm.hotspot.oops;
26  
27  import java.io.*;
28  import java.util.*;
29  import sun.jvm.hotspot.code.*;
30  import sun.jvm.hotspot.debugger.*;
31  import sun.jvm.hotspot.interpreter.*;
32  import sun.jvm.hotspot.memory.*;
33  import sun.jvm.hotspot.runtime.*;
34  import sun.jvm.hotspot.types.*;
35  import sun.jvm.hotspot.utilities.*;
36  
37  public class ConstMethod extends VMObject {
38    static {
39      VM.registerVMInitializedObserver(new Observer() {
40          public void update(Observable o, Object data) {
41            initialize(VM.getVM().getTypeDataBase());
42          }
43        });
44    }
45  
46    // anon-enum constants for _flags.
47    private static int HAS_LINENUMBER_TABLE;
48    private static int HAS_CHECKED_EXCEPTIONS;
49    private static int HAS_LOCALVARIABLE_TABLE;
50    private static int HAS_EXCEPTION_TABLE;
51    private static int HAS_GENERIC_SIGNATURE;
52    private static int HAS_METHOD_ANNOTATIONS;
53    private static int HAS_PARAMETER_ANNOTATIONS;
54    private static int HAS_METHOD_PARAMETERS;
55    private static int HAS_DEFAULT_ANNOTATIONS;
56    private static int HAS_TYPE_ANNOTATIONS;
57  
58    private static final int sizeofShort = 2;
59  
60    private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
61      Type type                  = db.lookupType("ConstMethod");
62      constants                  = new MetadataField(type.getAddressField("_constants"), 0);
63      constMethodSize            = new CIntField(type.getCIntegerField("_constMethod_size"), 0);
64      flags                      = new CIntField(type.getCIntegerField("_flags"), 0);
65  
66      // enum constants for flags
67      HAS_LINENUMBER_TABLE      = db.lookupIntConstant("ConstMethod::_has_linenumber_table").intValue();
68      HAS_CHECKED_EXCEPTIONS     = db.lookupIntConstant("ConstMethod::_has_checked_exceptions").intValue();
69      HAS_LOCALVARIABLE_TABLE   = db.lookupIntConstant("ConstMethod::_has_localvariable_table").intValue();
70      HAS_EXCEPTION_TABLE       = db.lookupIntConstant("ConstMethod::_has_exception_table").intValue();
71      HAS_GENERIC_SIGNATURE     = db.lookupIntConstant("ConstMethod::_has_generic_signature").intValue();
72      HAS_METHOD_ANNOTATIONS    = db.lookupIntConstant("ConstMethod::_has_method_annotations").intValue();
73      HAS_PARAMETER_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_parameter_annotations").intValue();
74      HAS_METHOD_PARAMETERS = db.lookupIntConstant("ConstMethod::_has_method_parameters").intValue();
75      HAS_DEFAULT_ANNOTATIONS   = db.lookupIntConstant("ConstMethod::_has_default_annotations").intValue();
76      HAS_TYPE_ANNOTATIONS      = db.lookupIntConstant("ConstMethod::_has_type_annotations").intValue();
77  
78      // Size of Java bytecodes allocated immediately after ConstMethod*.
79      codeSize                   = new CIntField(type.getCIntegerField("_code_size"), 0);
80      nameIndex                  = new CIntField(type.getCIntegerField("_name_index"), 0);
81      signatureIndex             = new CIntField(type.getCIntegerField("_signature_index"), 0);
82      idnum                      = new CIntField(type.getCIntegerField("_method_idnum"), 0);
83      maxStack                   = new CIntField(type.getCIntegerField("_max_stack"), 0);
84      maxLocals                  = new CIntField(type.getCIntegerField("_max_locals"), 0);
85      sizeOfParameters           = new CIntField(type.getCIntegerField("_size_of_parameters"), 0);
86  
87      // start of byte code
88      bytecodeOffset = type.getSize();
89  
90      type                       = db.lookupType("MethodParametersElement");
91      methodParametersElementSize = type.getSize();
92  
93      type                       = db.lookupType("CheckedExceptionElement");
94      checkedExceptionElementSize = type.getSize();
95  
96      type                       = db.lookupType("LocalVariableTableElement");
97      localVariableTableElementSize = type.getSize();
98  
99      type                       = db.lookupType("ExceptionTableElement");
100     exceptionTableElementSize = type.getSize();
101   }
102 
103   public ConstMethod(Address addr) {
104     super(addr);
105   }
106 
107   // Fields
108   private static MetadataField constants;
109   private static CIntField constMethodSize;
110   private static CIntField flags;
111   private static CIntField codeSize;
112   private static CIntField nameIndex;
113   private static CIntField signatureIndex;
114   private static CIntField idnum;
115   private static CIntField maxStack;
116   private static CIntField maxLocals;
117   private static CIntField sizeOfParameters;
118 
119   // start of bytecode
120   private static long bytecodeOffset;
121   private static long methodParametersElementSize;
122   private static long checkedExceptionElementSize;
123   private static long localVariableTableElementSize;
124   private static long exceptionTableElementSize;
125 
126   public Method getMethod() {
127     InstanceKlass ik = (InstanceKlass)getConstants().getPoolHolder();
128     MethodArray methods = ik.getMethods();
129     return methods.at((int)getIdNum());
130   }
131 
132   // Accessors for declared fields
133   public ConstantPool getConstants() {
134     return (ConstantPool) constants.getValue(this);
135   }
136 
137   public long getConstMethodSize() {
138     return constMethodSize.getValue(this);
139   }
140 
141   public long getFlags() {
142     return flags.getValue(this);
143   }
144 
145   public long getCodeSize() {
146     return codeSize.getValue(this);
147   }
148 
149   public long getNameIndex() {
150     return nameIndex.getValue(this);
151   }
152 
153   public long getSignatureIndex() {
154     return signatureIndex.getValue(this);
155   }
156 
157   public long getGenericSignatureIndex() {
158     if (hasGenericSignature()) {
159       return getAddress().getCIntegerAt(offsetOfGenericSignatureIndex(), 2, true);
160     } else {
161       return 0;
162     }
163   }
164 
165   public long getIdNum() {
166     return idnum.getValue(this);
167   }
168 
169   public long getMaxStack() {
170     return maxStack.getValue(this);
171   }
172 
173   public long getMaxLocals() {
174     return maxLocals.getValue(this);
175   }
176 
177   public long getSizeOfParameters() {
178     return sizeOfParameters.getValue(this);
179   }
180 
181   public Symbol getName() {
182     return getMethod().getName();
183   }
184 
185   public Symbol getSignature() {
186     return getMethod().getSignature();
187   }
188 
189   public Symbol getGenericSignature() {
190     return getMethod().getGenericSignature();
191   }
192 
193   // bytecode accessors
194 
195   /** Get a bytecode or breakpoint at the given bci */
196   public int getBytecodeOrBPAt(int bci) {
197     return getAddress().getJByteAt(bytecodeOffset + bci) & 0xFF;
198   }
199 
200   public byte getBytecodeByteArg(int bci) {
201     return (byte) getBytecodeOrBPAt(bci);
202   }
203 
204   /** Fetches a 16-bit big-endian ("Java ordered") value from the
205       bytecode stream */
206   public short getBytecodeShortArg(int bci) {
207     int hi = getBytecodeOrBPAt(bci);
208     int lo = getBytecodeOrBPAt(bci + 1);
209     return (short) ((hi << 8) | lo);
210   }
211 
212   /** Fetches a 16-bit native ordered value from the
213       bytecode stream */
214   public short getNativeShortArg(int bci) {
215     int hi = getBytecodeOrBPAt(bci);
216     int lo = getBytecodeOrBPAt(bci + 1);
217     if (VM.getVM().isBigEndian()) {
218         return (short) ((hi << 8) | lo);
219     } else {
220         return (short) ((lo << 8) | hi);
221     }
222   }
223 
224   /** Fetches a 32-bit big-endian ("Java ordered") value from the
225       bytecode stream */
226   public int getBytecodeIntArg(int bci) {
227     int b4 = getBytecodeOrBPAt(bci);
228     int b3 = getBytecodeOrBPAt(bci + 1);
229     int b2 = getBytecodeOrBPAt(bci + 2);
230     int b1 = getBytecodeOrBPAt(bci + 3);
231 
232     return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
233   }
234 
235   /** Fetches a 32-bit native ordered value from the
236       bytecode stream */
237   public int getNativeIntArg(int bci) {
238     int b4 = getBytecodeOrBPAt(bci);
239     int b3 = getBytecodeOrBPAt(bci + 1);
240     int b2 = getBytecodeOrBPAt(bci + 2);
241     int b1 = getBytecodeOrBPAt(bci + 3);
242 
243     if (VM.getVM().isBigEndian()) {
244         return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
245     } else {
246         return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
247     }
248   }
249 
250   public byte[] getByteCode() {
251      byte[] bc = new byte[ (int) getCodeSize() ];
252      for( int i=0; i < bc.length; i++ )
253      {
254         long offs = bytecodeOffset + i;
255         bc[i] = getAddress().getJByteAt( offs );
256      }
257      return bc;
258   }
259 
260   public long getSize() {
261     return getConstMethodSize();
262   }
263 
264   public void printValueOn(PrintStream tty) {
265     tty.print("ConstMethod " + getName().asString() + getSignature().asString() + "@" + getAddress());
266   }
267 
268   public void iterateFields(MetadataVisitor visitor) {
269     visitor.doMetadata(constants, true);
270       visitor.doCInt(constMethodSize, true);
271       visitor.doCInt(flags, true);
272       visitor.doCInt(codeSize, true);
273       visitor.doCInt(nameIndex, true);
274       visitor.doCInt(signatureIndex, true);
275       visitor.doCInt(codeSize, true);
276       visitor.doCInt(maxStack, true);
277       visitor.doCInt(maxLocals, true);
278       visitor.doCInt(sizeOfParameters, true);
279     }
280 
281   // Accessors
282 
283   public boolean hasLineNumberTable() {
284     return (getFlags() & HAS_LINENUMBER_TABLE) != 0;
285   }
286 
287   public int getLineNumberFromBCI(int bci) {
288     if (!VM.getVM().isCore()) {
289       if (bci == DebugInformationRecorder.SYNCHRONIZATION_ENTRY_BCI) bci = 0;
290     }
291 
292     if (isNative()) {
293       return -1;
294     }
295 
296     if (Assert.ASSERTS_ENABLED) {
297       Assert.that(bci == 0 || 0 <= bci && bci < getCodeSize(), "illegal bci");
298     }
299     int bestBCI  =  0;
300     int bestLine = -1;
301     if (hasLineNumberTable()) {
302       // The line numbers are a short array of 2-tuples [start_pc, line_number].
303       // Not necessarily sorted and not necessarily one-to-one.
304       CompressedLineNumberReadStream stream =
305         new CompressedLineNumberReadStream(getAddress(), (int) offsetOfCompressedLineNumberTable());
306       while (stream.readPair()) {
307         if (stream.bci() == bci) {
308           // perfect match
309           return stream.line();
310         } else {
311           // update best_bci/line
312           if (stream.bci() < bci && stream.bci() >= bestBCI) {
313             bestBCI  = stream.bci();
314             bestLine = stream.line();
315           }
316         }
317       }
318     }
319     return bestLine;
320   }
321 
322   public LineNumberTableElement[] getLineNumberTable() {
323     if (Assert.ASSERTS_ENABLED) {
324       Assert.that(hasLineNumberTable(),
325                   "should only be called if table is present");
326     }
327     int len = getLineNumberTableLength();
328     CompressedLineNumberReadStream stream =
329       new CompressedLineNumberReadStream(getAddress(), (int) offsetOfCompressedLineNumberTable());
330     LineNumberTableElement[] ret = new LineNumberTableElement[len];
331 
332     for (int idx = 0; idx < len; idx++) {
333       stream.readPair();
334       ret[idx] = new LineNumberTableElement(stream.bci(), stream.line());
335     }
336     return ret;
337   }
338 
339   public boolean hasLocalVariableTable() {
340     return (getFlags() & HAS_LOCALVARIABLE_TABLE) != 0;
341   }
342 
343   public Symbol getLocalVariableName(int bci, int slot) {
344     return getMethod().getLocalVariableName(bci, slot);
345   }
346 
347   /** Should only be called if table is present */
348   public LocalVariableTableElement[] getLocalVariableTable() {
349     if (Assert.ASSERTS_ENABLED) {
350       Assert.that(hasLocalVariableTable(), "should only be called if table is present");
351     }
352     LocalVariableTableElement[] ret = new LocalVariableTableElement[getLocalVariableTableLength()];
353     long offset = offsetOfLocalVariableTable();
354     for (int i = 0; i < ret.length; i++) {
355       ret[i] = new LocalVariableTableElement(getAddress(), offset);
356       offset += localVariableTableElementSize;
357     }
358     return ret;
359   }
360 
361   public boolean hasExceptionTable() {
362     return (getFlags() & HAS_EXCEPTION_TABLE) != 0;
363   }
364 
365   public ExceptionTableElement[] getExceptionTable() {
366     if (Assert.ASSERTS_ENABLED) {
367       Assert.that(hasExceptionTable(), "should only be called if table is present");
368     }
369     ExceptionTableElement[] ret = new ExceptionTableElement[getExceptionTableLength()];
370     long offset = offsetOfExceptionTable();
371     for (int i = 0; i < ret.length; i++) {
372       ret[i] = new ExceptionTableElement(getAddress(), offset);
373       offset += exceptionTableElementSize;
374     }
375     return ret;
376   }
377 
378   public boolean hasCheckedExceptions() {
379     return (getFlags() & HAS_CHECKED_EXCEPTIONS) != 0;
380   }
381 
382   public CheckedExceptionElement[] getCheckedExceptions() {
383     if (Assert.ASSERTS_ENABLED) {
384       Assert.that(hasCheckedExceptions(), "should only be called if table is present");
385     }
386     CheckedExceptionElement[] ret = new CheckedExceptionElement[getCheckedExceptionsLength()];
387     long offset = offsetOfCheckedExceptions();
388     for (int i = 0; i < ret.length; i++) {
389       ret[i] = new CheckedExceptionElement(getAddress(), offset);
390       offset += checkedExceptionElementSize;
391     }
392     return ret;
393   }
394 
395   private boolean hasMethodParameters() {
396     return (getFlags() & HAS_METHOD_PARAMETERS) != 0;
397   }
398 
399   private boolean hasGenericSignature() {
400     return (getFlags() & HAS_GENERIC_SIGNATURE) != 0;
401   }
402 
403   private boolean hasMethodAnnotations() {
404     return (getFlags() & HAS_METHOD_ANNOTATIONS) != 0;
405   }
406 
407   private boolean hasParameterAnnotations() {
408     return (getFlags() & HAS_PARAMETER_ANNOTATIONS) != 0;
409   }
410 
411   private boolean hasDefaultAnnotations() {
412     return (getFlags() & HAS_DEFAULT_ANNOTATIONS) != 0;
413   }
414 
415   private boolean hasTypeAnnotations() {
416     return (getFlags() & HAS_TYPE_ANNOTATIONS) != 0;
417   }
418 
419 
420   //---------------------------------------------------------------------------
421   // Internals only below this point
422   //
423 
424   private boolean isNative() {
425     return getMethod().isNative();
426   }
427 
428   // Offset of end of code
429   private long offsetOfCodeEnd() {
430     return bytecodeOffset + getCodeSize();
431   }
432 
433   // Offset of start of compressed line number table (see method.hpp)
434   private long offsetOfCompressedLineNumberTable() {
435     return offsetOfCodeEnd() + (isNative() ? 2 * VM.getVM().getAddressSize() : 0);
436   }
437 
438   // Offset of last short in Method* before annotations, if present
439   private long offsetOfLastU2Element() {
440     int offset = 0;
441     if (hasMethodAnnotations()) offset++;
442     if (hasParameterAnnotations()) offset++;
443     if (hasTypeAnnotations()) offset++;
444     if (hasDefaultAnnotations()) offset++;
445     long wordSize = VM.getVM().getObjectHeap().getOopSize();
446     return (getSize() * wordSize) - (offset * wordSize) - sizeofShort;
447   }
448 
449   // Offset of the generic signature index
450   private long offsetOfGenericSignatureIndex() {
451     return offsetOfLastU2Element();
452   }
453 
454   private long offsetOfMethodParametersLength() {
455     if (Assert.ASSERTS_ENABLED) {
456       Assert.that(hasMethodParameters(), "should only be called if table is present");
457     }
458     return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
459                                    offsetOfLastU2Element();
460   }
461 
462   private int getMethodParametersLength() {
463       if (hasMethodParameters())
464           return (int) getAddress().getCIntegerAt(offsetOfMethodParametersLength(), 2, true);
465       else
466           return 0;
467   }
468 
469   // Offset of start of checked exceptions
470   private long offsetOfMethodParameters() {
471     long offset = offsetOfMethodParametersLength();
472     long length = getMethodParametersLength();
473     if (Assert.ASSERTS_ENABLED) {
474       Assert.that(length > 0, "should only be called if method parameter information is present");
475     }
476     offset -= length * methodParametersElementSize;
477     return offset;
478   }
479 
480   private long offsetOfCheckedExceptionsLength() {
481     if (hasMethodParameters())
482       return offsetOfMethodParameters() - sizeofShort;
483     else {
484       return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
485                                      offsetOfLastU2Element();
486     }
487   }
488 
489   private int getCheckedExceptionsLength() {
490     if (hasCheckedExceptions()) {
491       return (int) getAddress().getCIntegerAt(offsetOfCheckedExceptionsLength(), 2, true);
492     } else {
493       return 0;
494     }
495   }
496 
497   // Offset of start of checked exceptions
498   private long offsetOfCheckedExceptions() {
499     long offset = offsetOfCheckedExceptionsLength();
500     long length = getCheckedExceptionsLength();
501     if (Assert.ASSERTS_ENABLED) {
502       Assert.that(length > 0, "should only be called if table is present");
503     }
504     offset -= length * checkedExceptionElementSize;
505     return offset;
506   }
507 
508   private int getLineNumberTableLength() {
509     int len = 0;
510     if (hasLineNumberTable()) {
511       CompressedLineNumberReadStream stream =
512         new CompressedLineNumberReadStream(getAddress(), (int) offsetOfCompressedLineNumberTable());
513       while (stream.readPair()) {
514         len += 1;
515       }
516     }
517     return len;
518   }
519 
520   private int getLocalVariableTableLength() {
521     if (hasLocalVariableTable()) {
522       return (int) getAddress().getCIntegerAt(offsetOfLocalVariableTableLength(), 2, true);
523     } else {
524       return 0;
525     }
526   }
527 
528   // Offset of local variable table length
529   private long offsetOfLocalVariableTableLength() {
530     if (Assert.ASSERTS_ENABLED) {
531       Assert.that(hasLocalVariableTable(), "should only be called if table is present");
532     }
533 
534     if (hasExceptionTable()) {
535       return offsetOfExceptionTable() - sizeofShort;
536     } else if (hasCheckedExceptions()) {
537       return offsetOfCheckedExceptions() - sizeofShort;
538     } else if (hasMethodParameters()) {
539       return offsetOfMethodParameters() - sizeofShort;
540     } else {
541       return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
542                                      offsetOfLastU2Element();
543     }
544   }
545 
546   private long offsetOfLocalVariableTable() {
547     long offset = offsetOfLocalVariableTableLength();
548     long length = getLocalVariableTableLength();
549     if (Assert.ASSERTS_ENABLED) {
550       Assert.that(length > 0, "should only be called if table is present");
551     }
552     offset -= length * localVariableTableElementSize;
553     return offset;
554   }
555 
556   private int getExceptionTableLength() {
557     if (hasExceptionTable()) {
558       return (int) getAddress().getCIntegerAt(offsetOfExceptionTableLength(), 2, true);
559     } else {
560       return 0;
561     }
562   }
563 
564   private long offsetOfExceptionTableLength() {
565     if (Assert.ASSERTS_ENABLED) {
566       Assert.that(hasExceptionTable(), "should only be called if table is present");
567     }
568     if (hasCheckedExceptions()) {
569       return offsetOfCheckedExceptions() - sizeofShort;
570     } else if (hasMethodParameters()) {
571       return offsetOfMethodParameters() - sizeofShort;
572     } else {
573       return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort :
574                                      offsetOfLastU2Element();
575     }
576   }
577 
578   private long offsetOfExceptionTable() {
579     long offset = offsetOfExceptionTableLength();
580     long length = getExceptionTableLength();
581     if (Assert.ASSERTS_ENABLED) {
582       Assert.that(length > 0, "should only be called if table is present");
583     }
584     offset -= length * exceptionTableElementSize;
585     return offset;
586   }
587 
588 }