This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFA/JVMTI] Implement GetLocalVariableTable and GetMaxLocals


Hi,

This patch implements the JVMTI method GetLocalVariableTable. This grabs the local variable table attribute out of a class file when it is loaded, similar to how a line table is obtained. When a JVMTI request for this information is given, it loads the information into a JVMTI appropriate structure, or reports the data is absent if the class file contained no table for this method.

This patch also includes the JVMTI GetMaxLocals method, which is useful when getting the local variable table. This just reads the max_locals field from a _Jv_InterpMethod.

Comments?

-Kyle

ChangeLog
2007-01-26  Kyle Galloway  <kgallowa@redhat.com>

	* defineclass.cc (_Jv_ClassReader::read_one_code_attribute):
	Added LocalVariableTable attribute handling.
	(_Jv_ClassReader::pool_Utf8_to_char_arr): New method.
	* jvmti.cc (_Jv_JVMTI_GetMaxLocals): New method.
	(_Jv_JVMTI_GetLocalVariableTable): New method.
	* include/java-interp.h: Added local_var_table and
	local_var_table_len fields to _Jv_InterpMethod.
	(_Jv_InterpMethod::get_max_locals): New method.
	(_Jv_InterpMethod::get_local_var_table): New method.
	* testsuite/libjava.jvmti/interp/getlocalvartable.java: New
	test.
	* testsuite/libjava.jvmti/interp/getlocalvartable.jar: New test.
	* testsuite/libjava.jvmti/interp/getlocalvartable.out: Output
	for new test.
	* testsuite/libjava.jvmti/interp/getlocalvartable.h: New test.
	* testsuite/libjava.jvmti/interp/natgetlocalvartable.cc: New
	test.



Index: libjava/include/java-interp.h
===================================================================
--- libjava/include/java-interp.h	(revision 121183)
+++ libjava/include/java-interp.h	(working copy)
@@ -137,6 +137,21 @@
   int line;
 };
 
+// This structure holds local variable information.
+// The pc value is the first pc where the variable must have a value and it
+// must continue to have a value until (start_pc + length).
+// The name is the variable name, and the descriptor contains type information.
+// The slot is the index in the local variable array of this method, long and
+// double occupy slot and slot+1.
+struct _Jv_LocalVarTableEntry
+{
+  int bytecode_start_pc;
+  int length;
+  char* name;
+  char* descriptor;
+  int slot;
+};
+
 class _Jv_InterpMethod : public _Jv_MethodBase
 {
   // Breakpoint instruction
@@ -157,6 +172,10 @@
   // Length of the line_table - when this is zero then line_table is NULL.
   int line_table_len;  
   _Jv_LineTableEntry *line_table;
+  
+  // The local variable table length and the table itself
+  int local_var_table_len;
+  _Jv_LocalVarTableEntry *local_var_table;
 
   pc_t prepared;
   int number_insn_slots;
@@ -219,7 +238,54 @@
    */
   void get_line_table (jlong& start, jlong& end, jintArray& line_numbers,
 		       jlongArray& code_indices);
+  
+  int get_max_locals ()
+  {
+    return static_cast<int> (max_locals);
+  }
 
+  /* Get info for a local variable of this method.
+   * If there is no loca_var_table for this method it will return -1.
+   * table_slot  indicates which slot in the local_var_table to get, if there is
+   * no variable at this location it will return 0.
+   * Otherwise, it will return the number of table slots after the selected
+   * slot, indexed from 0.
+   * 
+   * Example: there are 5 slots in the table, you request slot 0 so it will
+   * return 4.
+   */
+  int get_local_var_table (char **name, char **sig, char **generic_sig,
+                           long *startloc, jint *length, jint *slot,
+                           int table_slot)
+  {  	
+    if (local_var_table == NULL)
+      return -1;
+    if (table_slot >= local_var_table_len)
+      return 0;
+    else
+      {
+        *name = reinterpret_cast<char *> 
+          (_Jv_AllocBytes (strlen (local_var_table[table_slot].name) + 1));
+        strcpy (*name, local_var_table[table_slot].name);
+        
+        *sig = reinterpret_cast<char *> 
+          (_Jv_AllocBytes (
+             strlen (local_var_table[table_slot].descriptor) + 1));
+        strcpy (*sig, local_var_table[table_slot].descriptor);
+        
+        *generic_sig = reinterpret_cast<char *> 
+          (_Jv_AllocBytes (
+             strlen (local_var_table[table_slot].descriptor) + 1));
+        strcpy (*generic_sig, local_var_table[table_slot].descriptor);
+        
+        *startloc = static_cast<long> 
+                     (local_var_table[table_slot].bytecode_start_pc);
+        *length = static_cast<jint> (local_var_table[table_slot].length);
+        *slot = static_cast<jint> (local_var_table[table_slot].slot);
+      }
+    return local_var_table_len - table_slot - 1;
+  }
+
   /* Installs a break instruction at the given code index. Returns
      the pc_t of the breakpoint or NULL if index is invalid. */
   pc_t install_break (jlong index);
Index: libjava/testsuite/libjava.jvmti/interp/getlocalvartable.out
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/getlocalvartable.out	(revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/getlocalvartable.out	(revision 0)
@@ -0,0 +1,109 @@
+JVMTI getlocalvartable Interpreted Test
+Slot: 0
+  Name: this
+  Sig: Lgetlocalvartable;
+  Gen Sig: Lgetlocalvartable;
+  Start Loc: 0
+  Length: 28
+Slot: 1
+  Name: pone
+  Sig: F
+  Gen Sig: F
+  Start Loc: 0
+  Length: 28
+Slot: 2
+  Name: ptwo
+  Sig: F
+  Gen Sig: F
+  Start Loc: 0
+  Length: 28
+Slot: 3
+  Name: fone
+  Sig: F
+  Gen Sig: F
+  Start Loc: 2
+  Length: 26
+Slot: 4
+  Name: ftwo
+  Sig: F
+  Gen Sig: F
+  Start Loc: 7
+  Length: 21
+Slot: 5
+  Name: done
+  Sig: D
+  Gen Sig: D
+  Start Loc: 14
+  Length: 14
+Slot: 7
+  Name: dtwo
+  Sig: D
+  Gen Sig: D
+  Start Loc: 22
+  Length: 6
+Slot: 0
+  Name: this
+  Sig: Lgetlocalvartable;
+  Gen Sig: Lgetlocalvartable;
+  Start Loc: 0
+  Length: 25
+Slot: 1
+  Name: ipone
+  Sig: I
+  Gen Sig: I
+  Start Loc: 0
+  Length: 25
+Slot: 2
+  Name: iptwo
+  Sig: I
+  Gen Sig: I
+  Start Loc: 0
+  Length: 25
+Slot: 3
+  Name: ione
+  Sig: I
+  Gen Sig: I
+  Start Loc: 2
+  Length: 23
+Slot: 4
+  Name: itwo
+  Sig: I
+  Gen Sig: I
+  Start Loc: 7
+  Length: 18
+Slot: 5
+  Name: lone
+  Sig: J
+  Gen Sig: J
+  Start Loc: 11
+  Length: 14
+Slot: 7
+  Name: ltwo
+  Sig: J
+  Gen Sig: J
+  Start Loc: 19
+  Length: 6
+Slot: 0
+  Name: this
+  Sig: Lgetlocalvartable;
+  Gen Sig: Lgetlocalvartable;
+  Start Loc: 0
+  Length: 8
+Slot: 1
+  Name: op
+  Sig: Ljava/lang/Object;
+  Gen Sig: Ljava/lang/Object;
+  Start Loc: 0
+  Length: 8
+Slot: 2
+  Name: oone
+  Sig: Ljava/lang/Object;
+  Gen Sig: Ljava/lang/Object;
+  Start Loc: 2
+  Length: 6
+Slot: 3
+  Name: otwo
+  Sig: Ljava/lang/Object;
+  Gen Sig: Ljava/lang/Object;
+  Start Loc: 4
+  Length: 4
Index: libjava/testsuite/libjava.jvmti/interp/getlocalvartable.h
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/getlocalvartable.h	(revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/getlocalvartable.h	(revision 0)
@@ -0,0 +1,19 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+
+#include <jni.h>
+
+#ifndef __getlocalvartable__
+#define __getlocalvartable__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+JNIEXPORT jint JNICALL Java_getlocalvartable_do_1getlocalvartable_1tests (JNIEnv *env, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __getlocalvartable__ */
Index: libjava/testsuite/libjava.jvmti/interp/natgetlocalvartable.cc
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/natgetlocalvartable.cc	(revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/natgetlocalvartable.cc	(revision 0)
@@ -0,0 +1,67 @@
+#include <jni.h>
+
+#include <jvmti.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "getlocalvartable.h"
+
+JNIEXPORT jint JNICALL Java_getlocalvartable_do_1getlocalvartable_1tests
+(JNIEnv *env, jclass klass)
+{
+  JavaVM *vm;
+  jint err = env->GetJavaVM (&vm);
+  if (err < 0)
+    {
+      fprintf (stderr, "error getting VM\n");
+      exit (1);
+    }
+
+  jvmtiEnv *jvmti = NULL;
+  vm->GetEnv ((void **) &jvmti, JVMTI_VERSION_1_0);
+
+  if (jvmti == NULL)
+    {
+      fprintf (stderr, "error getting jvmti environment\n");
+      exit (1);
+    }
+  
+  jint entrys;
+  jvmtiLocalVariableEntry *var_table;
+
+  jvmtiError jerr;
+  
+  jmethodID meth_ids[3];
+  
+  meth_ids[0] = env->GetMethodID (klass, "aMethod", "(FF)D");
+  meth_ids[1] = env->GetMethodID (klass, "bMethod", "(II)J");
+  meth_ids[2] = env->GetMethodID (klass, "cMethod", 
+                                  "(Ljava/lang/Object;)Ljava/lang/Object;");
+  for (int i = 0; i < 3; i++)
+    {
+      jerr = jvmti->GetLocalVariableTable (meth_ids[i], &entrys, &var_table);
+      if (jerr != JVMTI_ERROR_NONE)
+        {
+          char *error_name;
+          jvmti->GetErrorName (jerr, &error_name);
+          fprintf (stderr, "JVMTI Error: %s\n", error_name);
+          jvmti->Deallocate (reinterpret_cast<unsigned char *> (error_name));
+        }
+      else
+        {
+          for (int j = 0; j < entrys; j++)
+            {
+              printf ("Slot: %d\n", static_cast<int> (var_table[j].slot));
+              printf ("  Name: %s\n", var_table[j].name);
+              printf ("  Sig: %s\n", var_table[j].signature);
+              printf ("  Gen Sig: %s\n", var_table[j].generic_signature);
+              printf ("  Start Loc: %ld\n", static_cast<long> (var_table[j].start_location));
+              printf ("  Length: %d\n", static_cast<int> (var_table[j].length));
+            }
+          
+          jvmti->Deallocate (reinterpret_cast<unsigned char *> (var_table));
+        }
+    }
+    
+    return 0;
+}
Index: libjava/testsuite/libjava.jvmti/interp/getlocalvartable.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: libjava/testsuite/libjava.jvmti/interp/getlocalvartable.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: libjava/testsuite/libjava.jvmti/interp/getlocalvartable.java
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/getlocalvartable.java	(revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/getlocalvartable.java	(revision 0)
@@ -0,0 +1,63 @@
+public class getlocalvartable
+{
+  public boolean done = false;
+
+  // num_frames is the number of frames > the original run () call so if
+  // num_frames = 1, the thread will have 2 frames, the original Thread.run
+  // call, plus one additional
+  public int num_frames, thread_num;
+
+  public static int num_threads = 1;
+
+  static
+    {
+      System.loadLibrary("natgetlocalvartable");
+    }
+
+  public double aMethod (float pone, float ptwo)
+  {
+    float fone, ftwo;
+    double done, dtwo;
+    
+    fone = pone;
+    ftwo = 2 * ptwo;
+    
+    done = 5 * fone;
+    dtwo = 6 * ftwo;
+    
+    return done + dtwo;
+  }
+  
+  public long bMethod (int ipone, int iptwo)
+  {
+    int ione, itwo;
+    long lone, ltwo;
+    
+    ione = ipone;
+    itwo = 5 * iptwo;
+    
+    lone = ione;
+    ltwo = 8 * itwo;
+    
+    return lone + ltwo;
+  }
+  
+  public Object cMethod (Object op)
+  {
+    Object oone, otwo;
+    oone = op;
+    otwo = oone;
+    oone = null;
+    
+    return otwo;
+  }
+
+  public static native int do_getlocalvartable_tests ();
+
+  public static void main (String[] args)
+  {
+    System.out.println ("JVMTI getlocalvartable Interpreted Test");
+
+    do_getlocalvartable_tests ();
+  }
+}
Index: libjava/jvmti.cc
===================================================================
--- libjava/jvmti.cc	(revision 121183)
+++ libjava/jvmti.cc	(working copy)
@@ -149,6 +149,18 @@
     }						\
   while (0)
 
+#define CHECK_FOR_NATIVE_METHOD(AjmethodID)	\
+  do					\
+    {					\
+      jboolean is_native;		\
+      jvmtiError jerr = env->IsMethodNative (AjmethodID, &is_native);	\
+      if (jerr != JVMTI_ERROR_NONE)					\
+        return jerr;								\
+      if (is_native)								\
+        return JVMTI_ERROR_NATIVE_METHOD;			\
+    }										\
+  while (0)
+
 static jvmtiError JNICALL
 _Jv_JVMTI_SuspendThread (MAYBE_UNUSED jvmtiEnv *env, jthread thread)
 {
@@ -672,6 +684,77 @@
 }
 
 static jvmtiError JNICALL
+_Jv_JVMTI_GetLocalVariableTable (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                                 jint *num_locals,
+                                 jvmtiLocalVariableEntry **locals)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+  NULL_CHECK (num_locals);
+  NULL_CHECK (locals);
+  
+  CHECK_FOR_NATIVE_METHOD(method);
+  
+  jclass klass;
+  jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+  _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> 
+                              (_Jv_FindInterpreterMethod (klass, method));
+  
+  if (imeth == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+  
+  jerr = env->GetMaxLocals (method, num_locals);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  jerr = env->Allocate (static_cast<jlong> 
+                          ((*num_locals) * sizeof (jvmtiLocalVariableEntry)),
+                        reinterpret_cast<unsigned char **> (locals));
+  
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+  
+  //the slot in the methods local_var_table to get
+  int table_slot = 0;
+  
+  // Get the first variable, and check to make sure the table exists.
+  if (imeth->get_local_var_table (&((*locals)[table_slot].name),
+                                  &((*locals)[table_slot].signature),
+                                  &((*locals)[table_slot].generic_signature),
+                                  reinterpret_cast<long *> 
+                                   (&(((*locals)[table_slot].start_location))),
+                                  &((*locals)[table_slot].length), 
+                                  &((*locals)[table_slot].slot),
+                                  table_slot)
+      == -1)
+    return JVMTI_ERROR_ABSENT_INFORMATION;
+  
+  do
+    {
+      table_slot++;
+    }
+  while (table_slot < *num_locals 
+         && imeth->get_local_var_table (&((*locals)[table_slot].name),
+                                  &((*locals)[table_slot].signature),
+                                  &((*locals)[table_slot].generic_signature),
+                                  reinterpret_cast<long *>
+                                   (&(((*locals)[table_slot].start_location))),
+                                  &((*locals)[table_slot].length), 
+                                  &((*locals)[table_slot].slot),
+                                  table_slot) 
+            > 0);
+  
+  // If there are double or long variables in the table, the the table will be
+  // smaller than the max number of slots, so correct for this here.
+  if ((table_slot + 1) < *num_locals)
+    *num_locals = table_slot + 1;
+  
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
 _Jv_JVMTI_IsMethodNative (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
 			  jboolean *result)
 {
@@ -701,6 +784,31 @@
 }
 
 static jvmtiError JNICALL
+_Jv_JVMTI_GetMaxLocals (MAYBE_UNUSED jvmtiEnv *env, jmethodID method,
+                        jint *max_locals)
+{
+  REQUIRE_PHASE (env, JVMTI_PHASE_START | JVMTI_PHASE_LIVE);
+  NULL_CHECK (max_locals);
+  
+  CHECK_FOR_NATIVE_METHOD (method);
+  
+  jclass klass;
+  jvmtiError jerr = env->GetMethodDeclaringClass (method, &klass);
+  if (jerr != JVMTI_ERROR_NONE)
+    return jerr;
+
+  _Jv_InterpMethod *imeth = reinterpret_cast<_Jv_InterpMethod *> 
+                              (_Jv_FindInterpreterMethod (klass, method));
+    
+  if (imeth == NULL)
+    return JVMTI_ERROR_INVALID_METHODID;
+  
+  *max_locals = imeth->get_max_locals ();
+  
+  return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
 _Jv_JVMTI_GetMethodDeclaringClass (MAYBE_UNUSED jvmtiEnv *env,
 				   jmethodID method,
 				   jclass *declaring_class_ptr)
@@ -1543,11 +1651,11 @@
   _Jv_JVMTI_GetMethodDeclaringClass,  // GetMethodDeclaringClass
   _Jv_JVMTI_GetMethodModifiers,	// GetMethodModifers
   RESERVED,			// reserved67
-  UNIMPLEMENTED,		// GetMaxLocals
+  _Jv_JVMTI_GetMaxLocals,		// GetMaxLocals
   UNIMPLEMENTED,		// GetArgumentsSize
   _Jv_JVMTI_GetLineNumberTable,	// GetLineNumberTable
   UNIMPLEMENTED,		// GetMethodLocation
-  UNIMPLEMENTED,		// GetLocalVariableTable
+  _Jv_JVMTI_GetLocalVariableTable,		// GetLocalVariableTable
   RESERVED,			// reserved73
   RESERVED,			// reserved74
   UNIMPLEMENTED,		// GetBytecodes
Index: libjava/defineclass.cc
===================================================================
--- libjava/defineclass.cc	(revision 121183)
+++ libjava/defineclass.cc	(working copy)
@@ -299,6 +299,9 @@
 
   /** check an utf8 entry, without creating a Utf8Const object */
   bool is_attribute_name (int index, const char *name);
+  
+  /** return the value of a utf8 entry in the passed array */
+  int pool_Utf8_to_char_arr (int index, char **entry);
 
   /** here goes the class-loader members defined out-of-line */
   void handleConstantPool ();
@@ -784,6 +787,18 @@
     return !memcmp (bytes+offsets[index]+2, name, len);
 }
 
+// Get a UTF8 value from the constant pool and turn it into a garbage
+// collected char array.
+int _Jv_ClassReader::pool_Utf8_to_char_arr (int index, char** entry)
+{
+  check_tag (index, JV_CONSTANT_Utf8);
+  int len = get2u (bytes + offsets[index]);
+  *entry = reinterpret_cast<char *> (_Jv_AllocBytes (len + 1));
+  (*entry)[len] = '\0';
+  memcpy (*entry, bytes + offsets[index] + 2, len);
+  return len + 1;
+}
+
 void _Jv_ClassReader::read_one_field_attribute (int field_index,
 						bool *found_value)
 {
@@ -979,6 +994,31 @@
       method->line_table_len = table_len;
       method->line_table = table;
     }
+  else if (is_attribute_name (name, "LocalVariableTable"))
+    {
+      _Jv_InterpMethod *method = reinterpret_cast<_Jv_InterpMethod *>
+	                       (def_interp->interpreted_methods[method_index]);
+      if (method->local_var_table != NULL)
+        throw_class_format_error ("Method already has LocalVariableTable");
+	
+      int table_len = read2u ();
+      _Jv_LocalVarTableEntry *table 
+        = reinterpret_cast<_Jv_LocalVarTableEntry *>
+            (_Jv_AllocBytes (table_len * sizeof (_Jv_LocalVarTableEntry)));
+                               
+      for (int i = 0; i < table_len; i++)
+        {
+          table[i].bytecode_start_pc = read2u ();
+          table[i].length = read2u ();
+          int len;
+          len = pool_Utf8_to_char_arr (read2u (), &table[i].name);
+          len = pool_Utf8_to_char_arr (read2u (), &table[i].descriptor);
+          table[i].slot = read2u ();
+        }
+	    
+      method->local_var_table_len = table_len;
+      method->local_var_table = table;
+    }
   else
     {
       /* ignore unknown code attributes */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]