/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
class MethodInvocation {

  static boolean verbose = true;         // set to true to get messages for each test
  static boolean allTestPass = true;
  static int testFlagForVoid = 0;        // flag to be updated by method returning void

  public static native void setVerboseOff();

  /************************************************************
   * Declare native methods that will call the JNI method invocation
   */
  static native int invokeStaticMethodA(Object objArg);
  static native int invokeStaticMethodV(Object objArg);
  static native int invokeStaticMethod(Object objArg);
  static native int invokeVirtualMethodA(MethodInvocation targetObj, Object objArg);
  static native int invokeVirtualMethodV(MethodInvocation targetObj, Object objArg);
  static native int invokeVirtualMethod(MethodInvocation targetObj, Object objArg);
  static native int invokeNonVirtualMethodA(MethodInvocationSub targetObj, Object objArg);
  static native int invokeNonVirtualMethodV(MethodInvocationSub targetObj, Object objArg);
  static native int invokeNonVirtualMethod(MethodInvocationSub targetObj, Object objArg);



  /************************************************************
   * Static methods serving as target to be invoked from native code
   * receive arguments of every type, return one of each type
   */

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)Z        */
  public static boolean staticReturnBoolean(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return !val8;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)B        */
  public static byte staticReturnByte(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return (byte) (val0 + 3);
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)C        */
  public static char staticReturnChar(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    if (val1=='a')
      return 'b';
    else
      return 'c';    // didn't get the expected argument, force test to fail
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)S        */
  public static short staticReturnShort(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return (short) (val2 + 15);
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)I        */
  public static int staticReturnInt(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val3 + (int) val2;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)J        */
  public static long staticReturnLong(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val4 + (long) val3;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)F        */
  public static float staticReturnFloat(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val5 + (float) 100.0;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)D        */
  public static double staticReturnDouble(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val6 + (double) 100.0;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)         */
  public static void staticReturnVoid(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    testFlagForVoid = 789;     // update the flag to indicate success
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)Ljava/lang/Object;        */
  public static Object staticReturnObject(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return new String("Year 2000");
  }


  /************************************************************
   * Virtual methods serving as target to be invoked from native code
   * receive arguments of every type, return one of each type
   */

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)Z        */
  public boolean virtualReturnBoolean(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return !val8;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)B        */
  public byte virtualReturnByte(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return (byte) (val0 + 7);
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)C        */
  public char virtualReturnChar(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    if (val1=='x')
      return 'y';
    else
      return 'z';    // didn't get the expected argument, force test to fail
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)S        */
  public short virtualReturnShort(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return (short) (val2 + 23);
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)I        */
  public int virtualReturnInt(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val3 + (int) val0;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)J        */
  public long virtualReturnLong(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val4 + (long) val2;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)F        */
  public float virtualReturnFloat(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val5 + (float) 32.0;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)D        */
  public double virtualReturnDouble(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return val6 + (double) 1000.0;
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)         */
  public void virtualReturnVoid(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    testFlagForVoid = 456;     // update the flag to indicate success
  }

  /* The signature is:     (BCSIJFDLjava/lang/Object;Z)Ljava/lang/Object;        */
  public Object virtualReturnObject(byte val0, char val1, short val2,
                                  int val3, long val4, float val5,
                                  double val6, Object val7, boolean val8) {
    return new String("RVM");
  }


  /************************************************************
   * Dummy constructor to get to the virtual methods
   */
  public MethodInvocation() {

  }

  /************************************************************
   * Main body of the test program
 */
  public static void main(String[] args) {
    int returnValue;
    String anObj = new String("Year of the Dragon");

    System.loadLibrary("MethodInvocation");

    if (args.length!=0) {
      if (args[0].equals("-quiet")) {
        verbose = false;
        setVerboseOff();
      }
    }

    /**
     *  Test the static method invocations
     */
    // pass arguments as an array of jvalue
    returnValue = invokeStaticMethodA(anObj);
    checkTest(returnValue, "callStatic<type>MethodA");

    // pass arguments as va_list pointer to a variable argument list
    testFlagForVoid = 0;        // reset flag to be updated by method returning void
    returnValue = invokeStaticMethodV(anObj);
    checkTest(returnValue, "callStatic<type>MethodV");


    // pass arguments as a variable argument list at the call site
    testFlagForVoid = 0;        // reset flag to be updated by method returning void
    returnValue = invokeStaticMethod(anObj);
    checkTest(returnValue, "callStatic<type>Method");



    /**
     *  Test the virtual method invocations
     */
    // pass arguments as an array of jvalue
    MethodInvocation targetObj = new MethodInvocation();
    returnValue = invokeVirtualMethodA(targetObj, anObj);
    checkTest(returnValue, "call<type>MethodA");

    // pass arguments as va_list pointer to a variable argument list
    testFlagForVoid = 0;        // reset flag to be updated by method returning void
    returnValue = invokeVirtualMethodV(targetObj, anObj);
    checkTest(returnValue, "call<type>MethodV");

    // pass arguments as a variable argument list at the call site
    testFlagForVoid = 0;        // reset flag to be updated by method returning void
    returnValue = invokeVirtualMethod(targetObj, anObj);
    checkTest(returnValue, "call<type>Method");



    /**
     * Test the nonvirtual method invocations
     */
    MethodInvocationSub subObj = new MethodInvocationSub();

    // reset flag to be updated by method returning void
    testFlagForVoid = 0;
    MethodInvocationSub.testFlagForVoid = 0;

    // pass arguments as an array of jvalue
    returnValue = invokeNonVirtualMethodA(subObj, anObj);
    checkTest(returnValue, "callNonVirtual<type>MethodA");


    // reset flag to be updated by method returning void
    testFlagForVoid = 0;
    MethodInvocationSub.testFlagForVoid = 0;

    // pass arguments as va_list pointer to a variable argument list
    returnValue = invokeNonVirtualMethodV(subObj, anObj);
    checkTest(returnValue, "callNonVirtual<type>MethodV");


    // reset flag to be updated by method returning void
    testFlagForVoid = 0;
    MethodInvocationSub.testFlagForVoid = 0;

    // pass arguments as a variable argument list at the call site
    returnValue = invokeNonVirtualMethod(subObj, anObj);
    checkTest(returnValue, "callNonVirtual<type>Method");


    // Summarize tests

    if (allTestPass)
      System.out.println("PASS: MethodInvocation");
    else
      System.out.println("FAIL: MethodInvocation");


  }


  static void printVerbose(String str) {
    if (verbose)
      System.out.println(str);
  }

  static void checkTest(int returnValue, String testName) {
    if (returnValue==0) {
      printVerbose("PASS: " + testName);
    } else {
      allTestPass = false;
      printVerbose("FAIL: " + testName);
    }
  }
}
