OSDN Git Service

2012-05-21 Benjamin Kosnik <bkoz@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / interpret-run.cc
index 12d0b9a..a4c2d4d 100644 (file)
@@ -12,6 +12,8 @@ details.  */
  * compiled directly.  */
 
   using namespace java::lang::reflect;
+  
+  pc_t pc = NULL;
 
   // FRAME_DESC registers this particular invocation as the top-most
   // interpreter frame.  This lets the stack tracing code (for
@@ -20,13 +22,112 @@ details.  */
   // destructor so it cleans up automatically when the interpreter
   // returns.
   java::lang::Thread *thread = java::lang::Thread::currentThread();
+  
+#ifdef __GCJ_DEBUG
+  _Jv_InterpFrame frame_desc (meth, thread, NULL, &pc);
+#else
   _Jv_InterpFrame frame_desc (meth, thread);
+#endif
+
+#ifdef DIRECT_THREADED
+  ThreadCountAdjuster adj (meth, &frame_desc);
+#endif // DIRECT_THREADED
 
   _Jv_word stack[meth->max_stack];
   _Jv_word *sp = stack;
 
   _Jv_word locals[meth->max_locals];
 
+#ifdef __GCJ_DEBUG
+  // This is the information needed to get and set local variables with
+  // proper type checking.
+  frame_desc.locals = locals;
+  char locals_type[meth->max_locals];
+  frame_desc.locals_type = locals_type;
+  
+  // Set all slots as invalid until they are written to.
+  memset (locals_type, 'x', meth->max_locals);
+  
+  // We need to set the local variable types for the method arguments since
+  // they are valid at invocation.
+  
+  _Jv_Method *method = meth->get_method ();
+  int type_ctr = 0;
+  
+  // If the method is non-static, we need to set the type for the "this" pointer.
+  if ((method->accflags & java::lang::reflect::Modifier::STATIC) == 0)
+    {
+      if (args)
+        {
+          // Set the "this" pointer for this frame.
+          _Jv_word *this_ptr = reinterpret_cast<_Jv_word *> (args);
+          frame_desc.obj_ptr = this_ptr[0].o;
+        }
+
+      frame_desc.locals_type[0] = 'o';
+      type_ctr++;
+    }
+  
+  // Now parse the method signature to set the types of the other arguments.  
+  int sig_len = method->signature->len ();
+  char *signature = method->signature->chars ();
+  for (int i = 1; signature[i] != ')' && i <= sig_len; i++)
+    {
+      if (signature[i] == 'Z' || signature[i] == 'B' || signature[i] == 'C' 
+          || signature[i] == 'S' || signature[i] == 'I')
+        {
+          frame_desc.locals_type[type_ctr] = 'i';
+          type_ctr++;
+          continue;
+        }
+      else if (signature[i] == 'F')
+        {
+          frame_desc.locals_type[type_ctr] = 'f';
+          type_ctr++;
+          continue;
+        }
+      else if (signature[i] == 'J')
+        {
+          frame_desc.locals_type[type_ctr] = 'l';
+          frame_desc.locals_type[type_ctr+1] = 'x';
+          type_ctr += 2;
+          continue;
+        }
+      else if (signature[i] == 'D')
+        {
+          frame_desc.locals_type[type_ctr] = 'd';
+          frame_desc.locals_type[type_ctr+1] = 'x';
+          type_ctr += 2;
+          continue;
+        }
+      else if (signature[i] == 'L')
+        {
+          frame_desc.locals_type[type_ctr] = 'o';
+          type_ctr++;
+          while (signature[i] != ';')
+            i++;
+          continue;
+        }
+      else if (signature[i] == '[')
+        {
+          frame_desc.locals_type[type_ctr] = 'o';
+          type_ctr++;
+          
+          // Ignore multi-dimensional arrays.
+          while (signature[i] == '[')
+            i++;
+          
+          // Check for an object array
+          if (signature[i] == 'L')
+            {
+              while (signature[i] != ';')
+                i++;
+            }
+          continue;
+        }
+    }
+#endif /* __GCJ_DEBUG */
+
 #define INSN_LABEL(op) &&insn_##op
 
   static const void *const insn_target[] = 
@@ -244,30 +345,78 @@ details.  */
 #endif
   };
 
-  pc_t pc;
-
 #ifdef DIRECT_THREADED
 
-#ifdef DEBUG
+#ifdef __GCJ_DEBUG
 #undef NEXT_INSN
 #define NEXT_INSN                                                      \
   do                                                                   \
     {                                                                  \
+      pc_t insn = pc++;                                                        \
       if (JVMTI_REQUESTED_EVENT (SingleStep))                          \
        {                                                               \
          JNIEnv *env = _Jv_GetCurrentJNIEnv ();                        \
          jmethodID method = meth->self;                                \
-         jlocation loc = meth->insn_index (pc);                        \
+         jlocation loc = meth->insn_index (insn);                      \
          _Jv_JVMTI_PostEvent (JVMTI_EVENT_SINGLE_STEP, thread,         \
                               env, method, loc);                       \
        }                                                               \
-      goto *((pc++)->insn);                                            \
+      goto *(insn->insn);                                              \
     }                                                                  \
   while (0)
-#else
+
+// We fail to rewrite a breakpoint if there is another thread
+// currently executing this method.  This is a bug, but there's
+// nothing else we can do that doesn't cause a data race.
+#undef REWRITE_INSN
+#define REWRITE_INSN(INSN,SLOT,VALUE)                                  \
+  do                                                                   \
+    {                                                                  \
+      _Jv_MutexLock (&rewrite_insn_mutex);                             \
+      if (meth->thread_count <= 1)                                     \
+       {                                                               \
+         if (pc[-2].insn == breakpoint_insn->insn)                     \
+           {                                                           \
+             using namespace ::gnu::gcj::jvmti;                        \
+             jlocation location = meth->insn_index (pc - 2);           \
+             _Jv_RewriteBreakpointInsn (meth->self, location, (pc_t) INSN); \
+           }                                                           \
+         else                                                          \
+           pc[-2].insn = INSN;                                         \
+                                                                       \
+         pc[-1].SLOT = VALUE;                                          \
+       }                                                               \
+      _Jv_MutexUnlock (&rewrite_insn_mutex);                           \
+    }                                                                  \
+  while (0)
+
+#undef INTERP_REPORT_EXCEPTION
+#define INTERP_REPORT_EXCEPTION(Jthrowable) REPORT_EXCEPTION (Jthrowable)
+#else // !__GCJ_DEBUG
 #undef NEXT_INSN
 #define NEXT_INSN goto *((pc++)->insn)
-#endif
+
+// Rewriting a multi-word instruction in the presence of multiple
+// threads is a data race if a thread reads part of an instruction
+// while some other thread is rewriting that instruction.  We detect
+// more than one thread executing a method and don't rewrite the
+// instruction.  A thread entering a method blocks on
+// rewrite_insn_mutex until the write is complete.
+#define REWRITE_INSN(INSN,SLOT,VALUE)          \
+  do {                                         \
+    _Jv_MutexLock (&rewrite_insn_mutex);       \
+    if (meth->thread_count <= 1)               \
+      {                                                \
+       pc[-2].insn = INSN;                     \
+       pc[-1].SLOT = VALUE;                    \
+      }                                                \
+    _Jv_MutexUnlock (&rewrite_insn_mutex);     \
+  }                                            \
+  while (0)
+
+#undef INTERP_REPORT_EXCEPTION
+#define INTERP_REPORT_EXCEPTION(Jthrowable) /* not needed when not debugging */
+#endif // !__GCJ_DEBUG
 
 #define INTVAL() ((pc++)->int_val)
 #define AVAL() ((pc++)->datum)
@@ -301,7 +450,7 @@ details.  */
 
 #else
 
-#ifdef DEBUG
+#ifdef __GCJ_DEBUG
 #define NEXT_INSN                                                      \
   do                                                                   \
     {                                                                  \
@@ -416,8 +565,7 @@ details.  */
 #ifdef DIRECT_THREADED
        // Rewrite instruction so that we use a faster pre-resolved
        // method.
-       pc[-2].insn = &&invokevirtual_resolved;
-       pc[-1].datum = rmeth;
+       REWRITE_INSN (&&invokevirtual_resolved, datum, rmeth);
 #endif /* DIRECT_THREADED */
       }
       goto perform_invoke;
@@ -440,6 +588,7 @@ details.  */
          }
        else
          {
+           NULLCHECK (sp[0].o);
            jobject rcv = sp[0].o;
            _Jv_VTable *table = *(_Jv_VTable**) rcv;
            fun = (void (*)()) table->get_method (rmeth->method->index);
@@ -452,7 +601,7 @@ details.  */
       {
        /* here goes the magic again... */
        ffi_cif *cif = &rmeth->cif;
-       ffi_raw *raw = (ffi_raw*) sp;
+       INTERP_FFI_RAW_TYPE *raw = (INTERP_FFI_RAW_TYPE *) sp;
 
        _Jv_value rvalue;
 
@@ -1751,8 +1900,7 @@ details.  */
          }
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = newinsn;
-       pc[-1].datum = field->u.addr;
+       REWRITE_INSN (newinsn, datum, field->u.addr);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -1842,8 +1990,7 @@ details.  */
          }
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = newinsn;
-       pc[-1].int_val = field_offset;
+       REWRITE_INSN (newinsn, int_val, field_offset);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -1958,8 +2105,7 @@ details.  */
          }
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = newinsn;
-       pc[-1].datum = field->u.addr;
+       REWRITE_INSN (newinsn, datum, field->u.addr);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -2057,8 +2203,7 @@ details.  */
          }
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = newinsn;
-       pc[-1].int_val = field_offset;
+       REWRITE_INSN (newinsn, int_val, field_offset);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -2133,8 +2278,7 @@ details.  */
 #ifdef DIRECT_THREADED
        // Rewrite instruction so that we use a faster pre-resolved
        // method.
-       pc[-2].insn = &&invokespecial_resolved;
-       pc[-1].datum = rmeth;
+       REWRITE_INSN (&&invokespecial_resolved, datum, rmeth);
 #endif /* DIRECT_THREADED */
       }
       goto perform_invoke;
@@ -2171,8 +2315,7 @@ details.  */
 #ifdef DIRECT_THREADED
        // Rewrite instruction so that we use a faster pre-resolved
        // method.
-       pc[-2].insn = &&invokestatic_resolved;
-       pc[-1].datum = rmeth;
+       REWRITE_INSN (&&invokestatic_resolved, datum, rmeth);
 #endif /* DIRECT_THREADED */
       }
       goto perform_invoke;
@@ -2210,8 +2353,7 @@ details.  */
 #ifdef DIRECT_THREADED
        // Rewrite instruction so that we use a faster pre-resolved
        // method.
-       pc[-2].insn = &&invokeinterface_resolved;
-       pc[-1].datum = rmeth;
+       REWRITE_INSN (&&invokeinterface_resolved, datum, rmeth);
 #else
        // Skip dummy bytes.
        pc += 2;
@@ -2244,13 +2386,16 @@ details.  */
        /* VM spec, section 3.11.5 */
        if ((klass->getModifiers() & Modifier::ABSTRACT)
            || klass->isInterface())
-         throw new java::lang::InstantiationException;
+         {
+           jthrowable t = new java::lang::InstantiationException;
+           INTERP_REPORT_EXCEPTION (t);
+           throw t;
+         }
        jobject res = _Jv_AllocObject (klass);
        PUSHA (res);
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = &&new_resolved;
-       pc[-1].datum = klass;
+       REWRITE_INSN (&&new_resolved, datum, klass);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -2285,8 +2430,7 @@ details.  */
        PUSHA (result);
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = &&anewarray_resolved;
-       pc[-1].datum = klass;
+       REWRITE_INSN (&&anewarray_resolved, datum, klass);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -2313,7 +2457,9 @@ details.  */
     insn_athrow:
       {
        jobject value = POPA();
-       throw static_cast<jthrowable>(value);
+       jthrowable t = static_cast<jthrowable> (value);
+       INTERP_REPORT_EXCEPTION (t);
+       throw t;
       }
       NEXT_INSN;
 
@@ -2330,8 +2476,7 @@ details.  */
        PUSHA (value);
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = &&checkcast_resolved;
-       pc[-1].datum = to;
+       REWRITE_INSN (&&checkcast_resolved, datum, to);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -2358,8 +2503,7 @@ details.  */
        PUSHI (to->isInstance (value));
 
 #ifdef DIRECT_THREADED
-       pc[-2].insn = &&instanceof_resolved;
-       pc[-1].datum = to;
+       REWRITE_INSN (&&instanceof_resolved, datum, to);
 #endif /* DIRECT_THREADED */
       }
       NEXT_INSN;
@@ -2501,27 +2645,23 @@ details.  */
 
     insn_breakpoint:
       {
-       JvAssert (JVMTI_REQUESTED_EVENT (Breakpoint));
-
-       // Send JVMTI notification
        using namespace ::java::lang;
        jmethodID method = meth->self;
        jlocation location = meth->insn_index (pc - 1);
-       Thread *thread = Thread::currentThread ();
-       JNIEnv *jni_env = _Jv_GetCurrentJNIEnv ();
-
-       _Jv_JVMTI_PostEvent (JVMTI_EVENT_BREAKPOINT, thread, jni_env,
-                            method, location);
 
-       // Continue execution
        using namespace gnu::gcj::jvmti;
        Breakpoint *bp
          = BreakpointManager::getBreakpoint (reinterpret_cast<jlong> (method),
                                              location);
        JvAssert (bp != NULL);
 
+       // Save the insn here since the breakpoint could be removed
+       // before the JVMTI notification returns.
        pc_t opc = reinterpret_cast<pc_t> (bp->getInsn ());
 
+       bp->execute ();
+
+       // Continue execution
 #ifdef DIRECT_THREADED
        goto *(opc->insn);
 #else
@@ -2531,42 +2671,25 @@ details.  */
     }
   catch (java::lang::Throwable *ex)
     {
-#ifdef DIRECT_THREADED
-      void *logical_pc = (void *) ((insn_slot *) pc - 1);
-#else
-      int logical_pc = pc - 1 - meth->bytecode ();
+      // Check if the exception is handled and, if so, set the pc to the start
+      // of the appropriate catch block.
+      if (meth->check_handler (&pc, meth, ex))
+        {
+          sp = stack;
+          sp++->o = ex; // Push exception.
+#ifdef __GCJ_DEBUG
+          if (JVMTI_REQUESTED_EVENT (ExceptionCatch))
+            {
+              using namespace gnu::gcj::jvmti;
+              jlong catch_meth = reinterpret_cast<jlong> (meth->get_method ());
+              jlong catch_loc = meth->insn_index (pc);
+             _Jv_JVMTI_PostEvent (JVMTI_EVENT_EXCEPTION_CATCH, thread,
+                                  _Jv_GetCurrentJNIEnv (), catch_meth,
+                                  catch_loc, ex);
+            }
 #endif
-      _Jv_InterpException *exc = meth->exceptions ();
-      jclass exc_class = ex->getClass ();
-
-      for (int i = 0; i < meth->exc_count; i++)
-       {
-         if (PCVAL (exc[i].start_pc) <= logical_pc
-             && logical_pc < PCVAL (exc[i].end_pc))
-           {
-#ifdef DIRECT_THREADED
-             jclass handler = (jclass) exc[i].handler_type.p;
-#else
-             jclass handler = NULL;
-             if (exc[i].handler_type.i != 0)
-               handler = (_Jv_Linker::resolve_pool_entry (meth->defining_class,
-                                                          exc[i].handler_type.i)).clazz;
-#endif /* DIRECT_THREADED */
-
-             if (handler == NULL || handler->isAssignableFrom (exc_class))
-               {
-
-#ifdef DIRECT_THREADED
-                 pc = (insn_slot *) exc[i].handler_pc.p;
-#else
-                 pc = meth->bytecode () + exc[i].handler_pc.i;
-#endif /* DIRECT_THREADED */
-                 sp = stack;
-                 sp++->o = ex; // Push exception.
-                 NEXT_INSN;
-               }
-           }
-       }
+          NEXT_INSN;
+        }
 
       // No handler, so re-throw.
       throw ex;