/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package com.sun.cldchi.jvm;

public class DebuggerInvoke {


    private static final int T_BOOLEAN   =  4;
    private static final int T_CHAR      =  5;
    private static final int T_FLOAT     =  6;
    private static final int T_DOUBLE    =  7;
    private static final int T_BYTE      =  8;
    private static final int T_SHORT     =  9;
    private static final int T_INT       = 10;
    private static final int T_LONG      = 11;
    private static final int T_OBJECT    = 12;
    private static final int T_ARRAY     = 13;
    private static final int T_VOID      = 14;

    /**
     * This method is used to synchronously invoke a method on 
     * behalf of a debugger and then return the method return value back
     * to the debugger.  The reason for this sync method is the
     * non-synchronous nature of EntryActivations in the VM.  Picture this,
     * some event happens in the VM, like a breakpoint, which gets sent to
     * the debugger.  The debugger decides to invoke a Method.  The return
     * packet to the debugger for that invoke can't be sent until the Method
     * returns and we send the return value (or exception object!) back to 
     * the debugger.  So, all threads are suspended in the VM, we have
     * a Method in hand and the debugger has picked a thread to execute this
     * Method.  We create an entry activation for that Method with all the
     * args passed in by debugger.  Then we create another entry activattion
     * for this method.  This allows us to synchronously call the Method
     * requested, catch any exceptions and then call back down into the VM
     * to send the reply packet.  The return code in the VM will strip off
     * these method calls from the thread stack so the thread will be back
     * in the state it was before we started this whole invoke thing.  We 
     * use two different return call methods based on what type of caller
     * was on the stack when we started the whole invoke process.  This is
     * because there are two entry points into the VM; shared_call_vm and
     * shared_call_vm_oop. (Don't care about shared_call_vm_exception). We
     * need to make sure that the original call into the VM returns properly
     * including any return values (object or otherwise) stored on the stack
     * or in the EntryFrame or ThreadDesc.
     *
     * @param entry really an EntryActivation object from the VM
     * 
     * @param transport a Transport object from the VM
     *
     * @param id debugger request id used to generate outgoing reply packet
     *
     * @param options invoke options from debugger
     *
     * @param return_type return type of Method being invoked
     *
     * @param is_obj_return if original caller of VM was shared_call_vm_oop
     *
     */

    private static void debuggerInvoke(Object entry, Object transport, int id,
                                       int options, int return_type,
                                       int is_obj_return)
    {
      try {
        Object ret_obj = null;
        switch (return_type) {
        case T_VOID:
            invokeV(entry);
            break;
        case  T_OBJECT: {
            Object[] ret = new Object[1];
            Object o = invokeL(entry);
            ret[0] = o;
            ret_obj = ret;
            break;
        }

        case T_BOOLEAN:
        case T_BYTE:
        case T_SHORT:
        case T_CHAR:
        case T_INT: {
            int[] ret = new int[1];
            switch (return_type) {
            case T_BOOLEAN:
                boolean b = invokeZ(entry);
                ret[0] = b ? 1 : 0;
                break;
            case T_BYTE:
                ret[0] = (int)invokeB(entry);
                break;
            case T_SHORT:
                ret[0] = (int)invokeS(entry);
                break;
            case T_CHAR:
                ret[0] = (int)invokeC(entry);
                break;
            case T_INT:
                ret[0] = invokeI(entry);
                break;
            }
            ret_obj = ret;
            break;
        }
        case T_LONG: {
            long[] ret = new long[1];
            ret[0] = invokeJ(entry);
            ret_obj = ret;
            break;
        }
        case T_DOUBLE: {
            double[] ret = new double[1];
            ret[0] = invokeD(entry);
            ret_obj = ret;
            break;
        }
        case T_FLOAT: {
            float[] ret = new float[1];
            ret[0] = invokeF(entry);
            ret_obj = ret;
            break;
        }
        }
        if (is_obj_return == 1) {
            debuggerInvokeReturnObj(ret_obj, null, transport, id, options,
                                    return_type);
        } else {
            debuggerInvokeReturn(ret_obj, null, transport, id, options,
                                 return_type);
        }
      } catch (Throwable t) {
          // we send the exception object to the debugger via the native call
          if (is_obj_return == 1) {
              debuggerInvokeReturnObj(null, t, transport, id, options,
                                      return_type);
          } else {
              debuggerInvokeReturn(null, t, transport, id, options,
                                   return_type);
          }
      }
    }

    /**
     * This method is used by the java debugger code to signal the
     * synchronous end of a method invoke
     */

    private native static long debuggerInvokeReturn(Object ret,
                                                   Object exception,
                                                   Object transport,
                                                   int id,
                                                   int options,
                                                   int return_type);

    /**
     * You might think "Oh, I could combine this two methods into one
     * and pass an arg to distinguish them".  Don't!  This one returns
     * Object so that it enters the VM via shared_call_vm_oop.  When
     * we strip the frames off the stack in ObjectReferenceImpl::invoke_return
     * we need to make sure we return the original return value that was
     * stored in the EntryFrame back to the original caller.
     */
    private native static Object debuggerInvokeReturnObj(Object ret,
                                                   Object exception,
                                                   Object transport,
                                                   int id,
                                                   int options,
                                                   int return_type);

    private static native void    invokeV(Object entry);

    private static native boolean invokeZ(Object entry);
    private static native char    invokeC(Object entry);
    private static native float   invokeF(Object entry);
    private static native double  invokeD(Object entry);
    private static native byte    invokeB(Object entry);
    private static native short   invokeS(Object entry);
    private static native int     invokeI(Object entry);
    private static native long    invokeJ(Object entry);
    private static native Object  invokeL(Object entry);


}
