(****************************************************************************
 *
 *  Native.dpr
 *
 *  This file implements the native routines declared in Native.java. The
 *  code is commented and fairly straight-forward. Note that there is
 *  almost no error checking in this code, however, a few functions do
 *  implement some error checks as a way of demonstrating how and where
 *  to check.
 *
 *  This code was a port from Native.cpp, a C++ implementation, also
 *  developed by Matthew Mead.
 *
 *  HISTORY
 *    7-22-2000   Created.
 *   10-14-2000   Modified to reflect JEDI standard of coding.
 *    3-17-2002   Modified Java_Native_pass2DByteArray and
 *                Java_Native_initializeByteArray to call
 *                ReleaseByteArrayElements. This was causing the function
 *                to fail with J2SDK 1.4.0. The function was always incorrect
 *                but this wasn't apparent under older versions of the JRE.
 *
 *
*)
library Native;

uses
  {$IFDEF WIN32}
  Windows,
  {$ENDIF}
  SysUtils,
  JNI;

(****************************************************************************
 *  Java declaration:
 *     native public void displayHelloWorld();
 *
 *  The canonical first method.
 *
 *  Class:     Native
 *  Method:    displayHelloWorld
 *  Signature: ()V
 *)
procedure Java_Native_displayHelloWorld(PEnv: PJNIEnv; Obj: JObject); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
begin
  Writeln('Hello world!');
end;


(****************************************************************************
 *  Java declaration:
 *     native public void initializeByteArray(byte[] byteArray, int count, byte value);
 *
 *  Initializes an array 'Count' times with the Value 'Value'. This function
 *  is mainly used to test the performance of array accesses in native code.
 *
 *  Class:     Native
 *  Method:    initializeByteArray
 *  Signature: ([BIB)V
 *)
procedure Java_Native_initializeByteArray(PEnv: PJNIEnv; Obj: JObject; ByteArray: JByteArray; Count: JInt; Value: JByte); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF} overload;
var
  Elements: PJByte;
  PE: PJByte;
  Len: JSize;
  I, J: Longint;
  IsCopy: JBoolean;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get elements of the array
  Elements := JVM.GetByteArrayElements(ByteArray, IsCopy);

    // Get length of the array
  Len := JVM.GetArrayLength(ByteArray);

    // Loop through the array 'Count' times, assigning 'Value' to each
    // element.
  for I := 0 to Count - 1 do
  begin
    PE := Elements;
    for J := 0 to Len - 1 do
    begin
      PE^ := Value;
      Inc(PE);
    end;
  end;

    // Important! From Sun's documentation:
    // Since the returned array may be a copy of the Java array, changes made
    // to the returned array will not necessarily be reflected in the original
    // array until Release<PrimitiveType>ArrayElements() is called.
  JVM.ReleaseByteArrayElements(ByteArray, Elements, 0);

end;


(****************************************************************************
 *  Java declaration:
 *     native public String printLine(String text);
 *
 *      Prints a Java string, and returns a Java string
 *
 *  Class:     Native
 *  Method:    printLine
 *  Signature: (Ljava/lang/String;)Ljava/lang/String;
 *)
function Java_Native_printLine(PEnv: PJNIEnv; Obj: JObject; Value: JString): JString; {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Str: string;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Convert the JString into a string
  Str := JVM.JStringToString(Value);

    // Print the string to the screen
  Writeln('In Native code, string is: ', Str);

    // Create a new string and return it
  Result := JVM.NewStringUTF('String from Native code');
  JVM.Free;
end;


(****************************************************************************
 *  Java declaration:
 *     native public void printWXYZ();
 *
 *  Prints out four members of the Native object, w, x, y, and z. This
 *  function acts sort of like the traditional 'toString' method in Java.
 *  The members are declared in Native.java as such:
 *
 *  public String w;       // public Object
 *  public int x;          // public
 *  private int y;         // private (no protection here)
 *  public static int z;   // public static
 *
 *  The return Value from each GetFieldID call should be checked.
 *  I don't check because I'm trying to keep the focus on the calls.
 *
 *  Class:     Native
 *  Method:    printWXYZ
 *  Signature: ()V
 *)
procedure Java_Native_printWXYZ(PEnv: PJNIEnv; Obj: JObject); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  X, Y, Z: JInt;
  W: JString;
  FID: JFieldID;
  Cls: JClass;
  Str: string;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get the class associated with this object
  Cls := JVM.GetObjectClass(Obj);

    // w is a String
  FID := JVM.GetFieldID(Cls, 'w', 'Ljava/lang/String;');

    // Get the Object (String) w.
  W := JVM.GetObjectField(Obj, FID);

    // x is a non-static public field
  FID := JVM.GetFieldID(Cls, 'x', 'I');

    // Get the int
  X := JVM.GetIntField(Obj, FID);

    // y is a non-static private field, same as public here
  FID := JVM.GetFieldID(Cls, 'y', 'I');

    // Get the int
  Y := JVM.GetIntField(Obj, FID);

    // z is a _static_ public field, so call different method
  FID := JVM.GetStaticFieldID(Cls, 'z', 'I');

    // Get static int
  Z := JVM.GetStaticIntField(Cls, FID);

    // Convert Java string into Delphi string
  Str := JVM.JStringToString(W);

    // Sort of like the traditional 'toString' output
  Writeln(Format('[w = %s, x = %d, y = %d, z = %d]', [Str, X, Y, Z]));

  JVM.Free;
end;

{***********************************
Same as above but without the wrapper.
procedure Java_Native_printWXYZ(PEnv: PJNIEnv; Obj: JObject);
  X, Y, Z: JInt;
  W: JString;
  FID: JFieldID;
  Cls: JClass;
  Str: string;
  IsCopy: JBoolean;  // added for GetStringChars
  Chars: PJChar;     // added for GetStringChars
begin
    // Get the class associated with this object
  Cls := PEnv^.GetObjectClass(PEnv, Obj);

    // w is a String
  FID := PEnv^.GetFieldID(PEnv, Cls, 'w', 'Ljava/lang/String;');

    // Get the Object (String) w.
  W := PEnv^.GetObjectField(PEnv, Obj, FID);

    // x is a non-static public field
  FID := PEnv^.GetFieldID(PEnv, Cls, 'x', 'I');

    // Get the int
  X := PEnv^.GetIntField(PEnv, Obj, FID);

    // y is a non-static private field, same as public here
  FID := PEnv^.GetFieldID(PEnv, Cls, 'y', 'I');

    // Get the int
  Y := PEnv^.GetIntField(PEnv, Obj, FID);

    // z is a _static_ public field, so call different method
  FID := PEnv^.GetStaticFieldID(PEnv, Cls, 'z', 'I');

    // Get static int
  Z := PEnv^.GetStaticIntField(PEnv, Cls, FID);

    // Convert Java string into Delphi string
  Chars := PEnv^.GetStringChars(PEnv, W, IsCopy);
  Str := string(Chars);
  PEnv^.ReleaseStringChars(PEnv, W, Chars);

    // Sort of like the traditional 'toString' output
  Writeln(Format('[w = %s, x = %d, y = %d, z = %d]', [Str, X, Y, Z]));
end;
}

(****************************************************************************
 *  Java declaration:
 *     native public String toStringWithPrint();
 *
 *  This is the traditional 'toString' method with a twist. It calls back
 *  into Java to do the real work. There is a Native method called toString
 *  which formats the object's members for printing. This function simply
 *  calls it.
 *
 *  The net effect is this: The Java object calls a native method which,
 *  in turn, calls back to the Java object. The Java could have simply
 *  made a call to it's own 'toString' method. This of course is for
 *  demonstration purposes only. Don't do this at work!!
 *
 *  Class:     Native
 *  Method:    toStringWithPrint
 *  Signature: ()Ljava/lang/String;
 *)
function Java_Native_toStringWithPrint(PEnv: PJNIEnv; Obj: JObject): JString; {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Cls: JClass;
  Mid: JMethodID;
  S: JString;
  Str: string;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

  // This just simply tests the non-existence of ExceptionCheck in JDK 1.1
  // Not used in this example for any "real" purpose.
  { TODO: Move this into a separate function }
  try
    JVM.ExceptionCheck;
  except
    on e: EJNIUnsupportedMethodError do
      Writeln(e.Message);
  end;

    // Get the class associated with this object
  Cls := JVM.GetObjectClass(Obj);

    // Get the ID for its 'String toString()' method
  Mid := JVM.GetMethodID(Cls, 'toString', '()Ljava/lang/String;');

    // Call the method, which returns a String
  S := JVM.CallObjectMethodA(Obj, Mid, nil);

    // Convert the JString to a string and print it to the screen
  Str := JVM.JStringToString(S);
  Writeln(str);

    // Create a JString and return it
  Result := JVM.NewStringUTF(PAnsiChar(Str));

  JVM.Free;
end;


(****************************************************************************
 *  Java declaration:
 *     native public void printObjectArray(Object[] array);
 *
 *  Given an array of Objects, each element is printed using the
 *  'toString' method of the Object.
 *
 *  Class:     Native
 *  Method:    printObjectArray
 *  Signature: ([Ljava/lang/Object;Z)V
 *)
procedure Java_Native_printObjectArray(PEnv: PJNIEnv; Obj: JObject; ObjArray: JObjectArray; Print: JBoolean); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Cls: JClass;
  Mid: JMethodID;
  Ob: JObject;
  Element: JObject;
  S: JString;
  Str: string;
  Len, I: Integer;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get length of the array
  len := JVM.GetArrayLength(ObjArray);

    // Make sure we have at least one object in the array
  if Len < 1 then
    exit;

    // Get an element of the array so we can get the right 'toString' method
  Ob := JVM.GetObjectArrayElement(ObjArray, 0);

    // Get the class associated with this object
  Cls := JVM.GetObjectClass(Ob);

  if Cls = nil then
  begin
    Writeln('Can''t GetObjectClass');
    exit;
  end;

    // Get method ID for the 'toString' method of this object's class
  Mid := JVM.GetMethodID(Cls, 'toString', '()Ljava/lang/String;');

    // We will check this also
  if Mid = nil then
  begin
    Writeln('Can''t GetMethodID for toString');
    exit;
  end;

    // Loop the the array of objects and print out each one using
    // the 'toString' method of its ancestor class Object
  for I := 0 to Len - 1 do
  begin

      // Get the next element from the array
    Element := JVM.GetObjectArrayElement(ObjArray, i);

      // Call the 'toString' method, which returns a String representation
      // of Rectangle object.
    S := JVM.CallObjectMethodA(Element, Mid, nil);

      // The actual printing can be turned on/off. This was useful during
      // debugging when passing thousands of elements into the procedure.
    if Print <> False then
    begin
      Str := JVM.JStringToString(S);
      Writeln(Str);
    end;
  end;

  JVM.Free;
end;


(****************************************************************************
 *  Java declaration:
 *     native public jobjectarray returnRectArray(int size);
 *
 *  This function creates an array of 'size' Rectangles and returns them
 *  to the Java caller. First, the array is created, then each element
 *  is assigned a Rectangle object.
 *
 *  Class:     Native
 *  Method:    returnRectArray
 *  Signature: (I)[Ljava/awt/Rectangle;
 *)
function Java_Native_returnRectArray(PEnv: PJNIEnv; Obj: JObject; Size: JInt): JObjectArray; {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Cls: JClass;
  Mid: JMethodID;
  Element: JObject;
  JOArray: JObjectArray;
  I: Integer;
  Args: array[0..3] of JValue;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Find the Rectangle class
  Cls := JVM.FindClass('java/awt/Rectangle');
  if Cls = nil then
  begin
    Writeln('Can''t FindClass(java/awt/Rectangle');
    Result := nil;
    exit;
  end;

    // Get its constructor (the one that takes 4 integers)
  Mid := JVM.GetMethodID(Cls, '<init>', '(IIII)V');
  if Mid = nil then
  begin
    Writeln('Can''t get MethodID for Rectangle constructor');
    Result := nil;
    exit;
  end;

    // Allocate the array of Rectangles
  JOArray := JVM.NewObjectArray(Size, Cls, nil);

    // Now initialize each one to a Rectangle
  for I := 0 to Size - 1 do
  begin

      // Create a new Rectangle object
    Args[0].i := 0;
    Args[1].i := 0;
    Args[2].i := 5 * I;
    Args[3].i := 10 * I;
    Element := JVM.NewObjectA(Cls, Mid, @args);

      // Assign the Rectangle to an element of the array
    JVM.SetObjectArrayElement(JOArray, I, Element);

  end;

    // Return the array back to the Java client
  Result := JOArray;

  JVM.Free;
end;


(****************************************************************************
 *  Java declaration:
 *     native public void handleException();
 *
 *  Causes an exception in the JVM, but detects it and supresses it
 *
 *  Class:     Native
 *  Method:    handleException
 *  Signature: ()V
 *)
procedure Java_Native_handleException(PEnv: PJNIEnv; Obj: JObject); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Cls: JClass;
  AException: JThrowable;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get the class to which this object belongs
  Cls := JVM.GetObjectClass(Obj);

    // Attempt to get the ID of the 'nonexistent' member, which, not by
    // chance, doesn't exist!
  JVM.GetFieldID(Cls, 'nonexistent', 'Ljava/lang/String;');

    // Check for exception
  AException := JVM.ExceptionOccurred;

    // exception is non-zero if an exception occurred
  if (AException <> nil) then
  begin

    //Writeln('Exception occurred in Native code and was handled. This was the exception:');
    Writeln(Format('Exception handled in Main.cpp: %d', [Longint(AException)]));

      // This call will print out a description of the exception
    //JVM.ExceptionDescribe(PEnv);

      // Clear the exception so the JVM doesn't react to it after we handled it
    JVM.ExceptionClear;

  end;

  JVM.Free;
end;


(****************************************************************************
 *  Java declaration:
 *     native public void causeException();
 *
 *  Causes an exception in the JVM, but fails to catch it. Thus, it is
 *  propagated back to the JVM.
 *
 *  Class:     Native
 *  Method:    causeException
 *  Signature: ()V
 *)
procedure Java_Native_causeException(PEnv: PJNIEnv; Obj: JObject); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Cls: JClass;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get the class to which this object belongs
  Cls := JVM.GetObjectClass(Obj);

    // Attempt to get the ID of the 'nonexistent' member, which, not by
    // chance, doesn't exist!
  JVM.GetFieldID(Cls, 'nonexistent', 'Ljava/lang/String;');

    // An exception has occurred, but it has not been suppressed.
    // The JVM will detect it and catch it.
    // Because we don't call this: JVM.ExceptionClear(PEnv),
    // the JVM will react.

  JVM.Free;
end;


(****************************************************************************
 *  Java declaration:
 *     native public void pass2DByteArray(byte[][] array);
 *
 *  Pass a 2-D array of Bytes from Java. This method will retrieve each
 *  element from the arrays, print it, then multiply it by 10 and store
 *  the new Value back into the array. This is to show access/updating
 *  of 2-D arrays within native code. The process would be similar for
 *  3-D arrays.
 *
 *  Class:     Native
 *  Method:    pass2DByteArray
 *  Signature: ([[B)V
 *)
procedure Java_Native_pass2DByteArray(PEnv: PJNIEnv; Obj: JObject; Array2D: JObjectArray); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  NumArrays: JSize;
  Len: JSize;
  I, J: Integer;
  JBArray: JByteArray;
  Elements, Walker: PJByte;
  IsCopy: JBoolean;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get length of the array (number of arrays)
  NumArrays := JVM.GetArrayLength(Array2D);

  Writeln('In native method printing 2-D byte array.');
  Writeln('Each element is then multiplied by 10 and updated.');

    // Loop over each array
  for I := 0 to NumArrays - 1 do
  begin

      // Get the object at the i'th position (it's an array of Bytes)
    JBArray := JVM.GetObjectArrayElement(Array2D, i);

      // Get the length of this array of Bytes
    Len := JVM.GetArrayLength(JBArray);

      // Get the elements from the Byte array
    Elements := JVM.GetByteArrayElements(JBArray, IsCopy);

      // We will "walk" the array with this pointer
    Walker := Elements;

      // Print each element, then multiply the Value by 10
      // and put it back into the array. (This is to prove that
      // the array elements have changed when this function returns
      // to Java code.
    for J := 0 to Len - 1 do
    begin
      Writeln(Format('%d,%d = %d', [I, J, Walker^]));

        // Update the element (just multiply it by 10)
      Walker^ := Walker^ * 10;
      Inc(Walker);
    end;

      // Important! From Sun's documentation:
      // Since the returned array may be a copy of the Java array, changes made
      // to the returned array will not necessarily be reflected in the original
      // array until Release<PrimitiveType>ArrayElements() is called.
    JVM.ReleaseByteArrayElements(JBArray, Elements, 0);

  end;

  JVM.Free;
end;


(****************************************************************************
 *  This is a sample algorithm that I got from Microsoft's documentation.
 *  I used it as the initial test case. I may have modified it slightly.
 *
 *  The interesting point in this example is that the function is declared
 *  as 'static' in the Java code. The only difference at the native level
 *  is that the second parameter is a 'JClass' instead of a 'JObject'.
 *  Most functions are non-static, and so require a handle to the object
 *  rather than the class.
 *
 *  Java declaration:
 *    native public static int countPrimes(byte[] array);
 *
 *  Class:     Native
 *  Method:    countPrimes
 *  Signature: ([B)I
 *)
function Java_Native_countPrimes(PEnv: PJNIEnv; Cls: JClass; ByteArray: JByteArray): JInt; {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Count, Len: JSize;
  I, J: JSize;
  Body: PJByte;
  IsCopy: JBoolean;
  Ptr: PChar;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get the length of the array
  Len := JVM.GetArrayLength(ByteArray);

    // Get a pointer to the elements of the array. Only primitive elements
    // can be accessed this way.
  Body := JVM.GetByteArrayElements(ByteArray, IsCopy);

    // Since body is not a Delphi array (Body is of type PJByte), we need to use
    // a PChar (interchangeable with arrays) to access the java array using an
    // index (e.g.  Body[0] is illegal, but Ptr[0] is legal)
  Ptr := PChar(Body);

    // Initialize all elements
  for I := 0 to Len - 1 do
    Ptr[i] := #1;

  Count := 0;

    // Set each element at a non-prime index to 0
  for I := 2 to Len - 1 do
  begin
    if Ptr[I] <> #0 then
    begin
      J := I + I;
      while J < Len do
      begin
        Ptr[J] := #0;
        Inc(J, I);
      end;
      Inc(Count);
    end;
  end;

    // The number of primes between 1 and array.length
  Result := Count;

end;


(****************************************************************************
 *  Java declaration:
 *     native public void VoidVoid();
 *
 *  This function takes no arguments and returns nothing. Just used to
 *  demonstrate the most simplistic function call. Also used to time
 *  function call overhead in the simplest case.
 *
 *  Class:     Native
 *  Method:    VoidVoid
 *  Signature: ()V
 *)
procedure Java_Native_VoidVoid(PEnv: PJNIEnv; Obj: JObject); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
//var
  //JVM: TJNIEnv;
begin
    // These two lines of code take about 1 ms to execute on a PIII 450 MHz
  //JVM := TJNIEnv.Create(PEnv);
  //JVM.Free;
end;


(****************************************************************************
 *  A private native function that calls back into Java. Calls a void
 *  function just for timings.
 *
 *  Class:     Native
 *  Method:    callbackVoid
 *  Signature: (I)V
 *)
procedure Java_Native_callbackVoid(PEnv: PJNIEnv; Obj: JObject; Count: JInt); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Cls: JClass;
  Mid: JMethodID;
  I: JInt;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get the class that this object belongs to
  Cls := JVM.GetObjectClass(Obj);

    // Get the method ID for 'void voidFunc()'
  Mid := JVM.GetMethodID(Cls, 'voidFunc', '()V');

    // If we can't find the ID, just return. This kind of check should be in all
    // functions in this code, but it isn't (for brevity)
  if (Mid = nil) then
    exit;

    // Perform the callbacks
  for I := 0 to Count - 1 do
    JVM.CallVoidMethodA(Obj, Mid, nil);

end;


(****************************************************************************
 *  Java declaration:
 *     native public void printVersion();
 *
 *  Prints the version of the JDK being used.
 *
 *  Class:     Native
 *  Method:    printVersion
 *  Signature: ()V
 *)
procedure Java_Native_printVersion(PEnv: PJNIEnv; Obj: JObject); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  JVM: TJNIEnv;
  Major, Minor: JInt;
begin
  JVM := TJNIEnv.Create(PEnv);
  Major := JVM.MajorVersion;
  Minor := JVM.MinorVersion;
  Writeln(Format('The Java version is %d.%d', [Major, Minor]));
end;


(****************************************************************************
 *  Java declaration:
 *     native public void testAllTypesD();
 *
 *  Tests all data types. Shows how floats passed in open arrays to JNI
 *  methods are not handled correctly.
 *
 * Class:     Native
 * Method:    testAllTypesD
 * Signature: (ZBCIJFD)V
 *)
procedure Java_Native_testAllTypesD(PEnv: PJNIEnv; Obj: JObject; Bool: JBoolean; B: JByte; C: JChar; I: JInt; L: JLong; F: jfloat; D: jdouble); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
var
  Cls: JClass;
  Mid: JMethodID;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get the class associated with this object
  Cls := JVM.GetObjectClass(Obj);

    // The ID for the testAllTypes method
  Mid := JVM.GetMethodID(Cls, 'testAllTypes', '(ZBCIJFD)V');

    // Call it
  JVM.CallVoidMethod(Obj, Mid, [Bool, B, C, I, L, F, D]);

end;


(****************************************************************************
 *  Java declaration:
 *     native public void multiplyIntegers(int op1, int op2);
 *
 *  Multiplies 2 integers and returns the result.
 *
 *  Class:     Native
 *  Method:    multiplyIntegers
 *  Signature: (II)I
*)
function Java_Native_multiplyIntegers(PEnv: PJNIEnv; Obj: JObject; Op1: JInt; Op2: JInt): jint; {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
begin
  Result := Op1 * Op2;
end;


(****************************************************************************
 *  Java declaration:
 *     native public void initializeByteArrayOnce(byte[] byteArray, byte Value);
 *
 *  Initializes each element of an array with the value 'Value'. This function
 *  is an overloaded version of the one used to test performance.
 *
 *  Class:     Native
 *  Method:    initializeByteArrayOnce
 *  Signature: ([BB)V
 *)
procedure Java_Native_initializeByteArray(PEnv: PJNIEnv; Obj: JObject; ByteArray: JByteArray; Value: JByte); {$IFDEF WIN32} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF} overload;
var
  Elements: PJByte;
  PE: PJByte;
  Len: JSize;
  I: Longint;
  IsCopy: JBoolean;
  JVM: TJNIEnv;
begin
    // Create an instance of the Java environment
  JVM := TJNIEnv.Create(PEnv);

    // Get elements of the array.
  elements := JVM.GetByteArrayElements(ByteArray, IsCopy);

    // Get length of the array.
  Len := JVM.GetArrayLength(ByteArray);

    // Assign 'Value' to each element.
  PE := Elements;
  for I := 0 to Len - 1 do
  begin
    PE^ := Value;
    Inc(PE);
  end;

    // Important! From Sun's documentation:
    // Since the returned array may be a copy of the Java array, changes made
    // to the returned array will not necessarily be reflected in the original
    // array until Release<PrimitiveType>ArrayElements() is called.
  JVM.ReleaseByteArrayElements(ByteArray, Elements, 0);

  JVM.Free;
end;


(*******************************************************************************
  Make these routines available to Java.
*******************************************************************************)
exports
  Java_Native_initializeByteArray(PEnv: PJNIEnv; Obj: JObject; ByteArray: JByteArray; Count: JInt; Value: JByte) name 'Java_Native_initializeByteArray',
  Java_Native_initializeByteArray(PEnv: PJNIEnv; Obj: JObject; ByteArray: JByteArray; Value: JByte) name 'Java_Native_initializeByteArrayOnce',
  Java_Native_callbackVoid,
  Java_Native_causeException,
  Java_Native_countPrimes,
  Java_Native_displayHelloWorld,
  Java_Native_handleException,
  Java_Native_multiplyIntegers,
  Java_Native_pass2DByteArray,
  Java_Native_printLine,
  Java_Native_printObjectArray,
  Java_Native_printVersion,
  Java_Native_printWXYZ,
  Java_Native_returnRectArray,
  Java_Native_testAllTypesD,
  Java_Native_toStringWithPrint,
  Java_Native_VoidVoid;

begin
end.