diff a/doc/panama_jextract.html b/doc/panama_jextract.html --- /dev/null +++ b/doc/panama_jextract.html @@ -0,0 +1,992 @@ + + + + + + + panama_jextract + + + + + + + +

Using the jextract tool

+

jextract is a simple - but convenient - tool which generates a Java API from one or more native C headers. The tool can be obtained by building the foreign-jextract branch of Panama foreign repository.

+

Interacting with the jextract tool usually involves two steps:

+
    +
  1. Use the jextract tool to generate a java interface for some C header files
  2. +
  3. Write a Java program which invokes the wrapper API points generated by jextract
  4. +
+

The jextract tool provides some basic options in order to control how the extraction process works; these are listed below:

+ +

The remainder of this documents shows some basic usage examples of the jextract tool.

+

Hello World

+

Hello World C Header (helloworld.h)

+

+#ifndef helloworld_h
+#define helloworld_h
+
+extern void helloworld(void);
+
+#endif /* helloworld_h */
+
+

Hello World C Source (helloworld.c)

+

+#include <stdio.h>
+
+#include "helloworld.h"
+
+void helloworld(void) {
+    printf("Hello World!\n");
+}
+

Building Hello World

+

+cc -shared -o libhelloworld.dylib helloworld.c
+

jextract a Jar file for helloworld.h

+

+jextract -t org.hello -lhelloworld helloworld.h
+

Java program that uses extracted helloworld interface

+

+import static org.hello.helloworld_h.*;
+
+public class HelloWorld {
+    public static void main(String[] args) {
+        helloworld();
+    }
+}
+

Running the Java code that invokes helloworld

+

+java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign HelloWorld.java
+

Embedding Python interpreter in your Java program (Mac OS)

+

jextract Python.h

+

+jextract \
+  -l python2.7 \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/python2.7/ \
+  -t org.python \
+   /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/python2.7/Python.h
+

Java program that uses extracted Python interface

+

+import jdk.incubator.foreign.ResourceScope;
+import static jdk.incubator.foreign.CLinker.*;
+import static jdk.incubator.foreign.MemoryAddress.NULL;
+// import jextracted python 'header' class
+import static org.python.Python_h.*;
+import org.python.*;
+
+public class PythonMain {
+    public static void main(String[] args) {
+        String script = "print(sum([33, 55, 66])); print('Hello from Python!')\n";
+
+        Py_Initialize();
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var str = toCString(script, scope);
+            PyRun_SimpleStringFlags(str, NULL);
+            Py_Finalize();
+        }
+    }
+}
+

Running the Java code that calls Python interpreter

+

+java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+    -Djava.library.path=/System/Library/Frameworks/Python.framework/Versions/2.7/lib \
+    PythonMain.java
+

Using readline library from Java code (Mac OS)

+

jextract readline.h

+

+jextract \
+  -l readline -t org.unix \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+   /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/readline/readline.h
+

Java code that uses readline

+

+import jdk.incubator.foreign.ResourceScope;
+import static org.unix.readline_h.*;
+import static jdk.incubator.foreign.CLinker.*;
+import org.unix.*;
+
+public class Readline {
+    public static void main(String[] args) {
+       try (var scope = ResourceScope.newConfinedScope()) {
+            var url = toCString("name? ", scope);
+
+            // call "readline" API
+            var p = readline(url);
+
+            // print char* as is
+            System.out.println(p);
+            // convert char* ptr from readline as Java String & print it
+            System.out.println("Hello, " + toJavaString(p));
+
+            freeMemory(p);
+        }
+    }
+}
+

Running the java code that uses readline

+
java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+    -Djava.library.path=/usr/local/opt/readline/lib/ Readline.java
+
+

Using libcurl from Java (Mac OS)

+

jextract curl.h

+

+jextract -t org.unix -lcurl \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curl/ \
+  /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/curl/curl.h
+

Java code that uses libcurl

+

+import jdk.incubator.foreign.ResourceScope;
+import static jdk.incubator.foreign.MemoryAddress.NULL;
+import static org.jextract.curl_h.*;
+import static jdk.incubator.foreign.CLinker.*;
+import org.jextract.*;
+
+public class CurlMain {
+   public static void main(String[] args) {
+       var urlStr = args[0];
+       curl_global_init(CURL_GLOBAL_DEFAULT());
+       var curl = curl_easy_init();
+       if(!curl.equals(NULL)) {
+           try (var scope = ResourceScope.newConfinedScope()) {
+               var url = toCString(urlStr, scope);
+               curl_easy_setopt(curl, CURLOPT_URL(), url.address());
+               int res = curl_easy_perform(curl);
+               if (res != CURLE_OK()) {
+                   String error = toJavaString(curl_easy_strerror(res));
+                   System.out.println("Curl error: " + error);
+                   curl_easy_cleanup(curl);
+               }
+           }
+       }
+       curl_global_cleanup();
+   }
+}
+

Running the java code that uses libcurl

+

+# run this shell script by passing a URL as first argument
+java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+    -Djava.library.path=/usr/lib CurlMain.java $*
+

Using BLAS library

+

BLAS is a popular library that allows fast matrix and vector computation: http://www.netlib.org/blas/.

+

Installing OpenBLAS (Mac OS)

+

On Mac, blas is available as part of the OpenBLAS library: https://github.com/xianyi/OpenBLAS/wiki

+

OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version.

+

You can install openblas using HomeBrew

+

+brew install openblas
+

It installs include and lib directories under /usr/local/opt/openblas

+

jextracting cblas.h (MacOS)

+

The following command can be used to extract cblas.h on MacOs

+

+jextract -C "-D FORCE_OPENBLAS_COMPLEX_STRUCT" \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+  -l openblas -t blas /usr/local/opt/openblas/include/cblas.h
+

Java sample code that uses cblas library

+

+import jdk.incubator.foreign.MemoryAddress;
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import blas.*;
+import static blas.cblas_h.*;
+import static jdk.incubator.foreign.CLinker.*;
+
+public class TestBlas {
+    public static void main(String[] args) {
+        int Layout;
+        int transa;
+
+        double alpha, beta;
+        int m, n, lda, incx, incy, i;
+
+        Layout = CblasColMajor();
+        transa = CblasNoTrans();
+
+        m = 4; /* Size of Column ( the number of rows ) */
+        n = 4; /* Size of Row ( the number of columns ) */
+        lda = 4; /* Leading dimension of 5 * 4 matrix is 5 */
+        incx = 1;
+        incy = 1;
+        alpha = 1;
+        beta = 0;
+
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var allocator = SegmentAllocator.ofScope(scope);
+            var a = allocator.allocateArray(C_DOUBLE, new double[] {
+                1.0, 2.0, 3.0, 4.0,
+                1.0, 1.0, 1.0, 1.0,
+                3.0, 4.0, 5.0, 6.0,
+                5.0, 6.0, 7.0, 8.0
+            });
+            var x = allocator.allocateArray(C_DOUBLE, new double[] {
+                1.0, 2.0, 1.0, 1.0
+            });
+            var y = allocator.allocateArray(C_DOUBLE, n);
+
+            cblas_dgemv(Layout, transa, m, n, alpha, a, lda, x, incx, beta, y, incy);
+            /* Print y */
+            for (i = 0; i < n; i++) {
+                System.out.print(String.format(" y%d = %f\n", i, MemoryAccess.getDoubleAtIndex(y, i)));
+            }
+        }
+    }
+}
+

Compiling and running the above BLAS sample

+

+jextract \
+  -C "-D FORCE_OPENBLAS_COMPLEX_STRUCT" \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+  -l openblas -t blas /usr/local/opt/openblas/include/cblas.h
+

Using LAPACK library (Mac OS)

+

On Mac OS, lapack is installed under /usr/local/opt/lapack directory.

+

jextracting lapacke.h

+

+jextract \
+   -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+   -l lapacke -t lapack \
+   /usr/local/opt/lapack/include/lapacke.h
+

Java sample code that uses LAPACK library

+

+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.MemoryAddress;
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import lapack.*;
+import static lapack.lapacke_h.*;
+import static jdk.incubator.foreign.CLinker.*;
+
+public class TestLapack {
+    public static void main(String[] args) {
+
+        /* Locals */
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var allocator = SegmentAllocator.ofScope(scope);
+            var A = allocator.allocateArray(C_DOUBLE, new double[]{
+                    1, 2, 3, 4, 5, 1, 3, 5, 2, 4, 1, 4, 2, 5, 3
+            });
+            var b = allocator.allocateArray(C_DOUBLE, new double[]{
+                    -10, 12, 14, 16, 18, -3, 14, 12, 16, 16
+            });
+            int info, m, n, lda, ldb, nrhs;
+
+            /* Initialization */
+            m = 5;
+            n = 3;
+            nrhs = 2;
+            lda = 5;
+            ldb = 5;
+
+            /* Print Entry Matrix */
+            print_matrix_colmajor("Entry Matrix A", m, n, A, lda );
+            /* Print Right Rand Side */
+            print_matrix_colmajor("Right Hand Side b", n, nrhs, b, ldb );
+            System.out.println();
+
+            /* Executable statements */
+            //            printf( "LAPACKE_dgels (col-major, high-level) Example Program Results\n" );
+            /* Solve least squares problem*/
+            info = LAPACKE_dgels(LAPACK_COL_MAJOR(), (byte)'N', m, n, nrhs, A, lda, b, ldb);
+
+            /* Print Solution */
+            print_matrix_colmajor("Solution", n, nrhs, b, ldb );
+            System.out.println();
+            System.exit(info);
+        }
+    }
+
+    static void print_matrix_colmajor(String msg, int m, int n, MemorySegment mat, int ldm) {
+        int i, j;
+        System.out.printf("\n %s\n", msg);
+
+        for( i = 0; i < m; i++ ) {
+            for( j = 0; j < n; j++ ) System.out.printf(" %6.2f", MemoryAccess.getDoubleAtIndex(mat, i+j*ldm));
+            System.out.printf( "\n" );
+        }
+    }
+}
+

Compiling and running the above LAPACK sample

+

+java --enable-native-access=ALL-UNNAMED \
+    --add-modules jdk.incubator.foreign \
+    -Djava.library.path=/usr/local/opt/lapack/lib \
+    TestLapack.java
+

Using libproc library to list processes from Java (Mac OS)

+

jextract libproc.h

+

+jextract \
+  -t org.unix \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+  /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libproc.h
+

Java program that uses libproc to list processes

+

+import jdk.incubator.foreign.CLinker;
+import jdk.incubator.foreign.MemorySegment;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import org.unix.*;
+import static jdk.incubator.foreign.MemoryAddress.NULL;
+import static org.unix.libproc_h.*;
+
+public class LibprocMain {
+    private static final int NAME_BUF_MAX = 256;
+
+    public static void main(String[] args) {
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var allocator = SegmentAllocator.ofScope(scope);
+            // get the number of processes
+            int numPids = proc_listallpids(NULL, 0);
+            // allocate an array
+            var pids = allocator.allocateArray(CLinker.C_INT, numPids);
+            // list all the pids into the native array
+            proc_listallpids(pids, numPids);
+            // convert native array to java array
+            int[] jpids = pids.toIntArray();
+            // buffer for process name
+            var nameBuf = allocator.allocateArray(CLinker.C_CHAR, NAME_BUF_MAX);
+            for (int i = 0; i < jpids.length; i++) {
+                int pid = jpids[i];
+                // get the process name
+                proc_name(pid, nameBuf, NAME_BUF_MAX);
+                String procName = CLinker.toJavaString(nameBuf);
+                // print pid and process name
+                System.out.printf("%d %s\n", pid, procName);
+            }
+        }
+    }
+}
+

Compiling and running the libproc sample

+

+java --enable-native-access=ALL-UNNAMED \
+    --add-modules jdk.incubator.foreign \
+    -Djava.library.path=/usr/lib LibprocMain.java
+

Using libgit2 from Java (Mac OS)

+

Getting and building libgit2

+ +

jextract git2.h

+

+jextract \
+  -t com.github -lgit2 \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \
+  -I ${LIBGIT2_HOME}/include/ \
+  -I ${LIBGIT2_HOME}/include/git2 \
+  ${LIBGIT2_HOME}/include/git2.h
+

Java program that uses libgit2 to clone github repo

+

+import jdk.incubator.foreign.MemoryAddress;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import static com.github.git2_h.*;
+import static jdk.incubator.foreign.CLinker.*;
+import static jdk.incubator.foreign.MemoryAddress.NULL;
+import com.github.*;
+
+public class GitClone {
+    public static void main(String[] args) {
+          if (args.length != 2) {
+              System.err.println("java GitClone <url> <path>");
+              System.exit(1);
+          }
+          git_libgit2_init();
+          try (var scope = ResourceScope.newConfinedScope()) {
+              var allocator = SegmentAllocator.ofScope(scope);
+              var repo = allocator.allocate(C_POINTER);
+              var url = toCString(args[0], scope);
+              var path = toCString(args[1], scope);
+              System.out.println(git_clone(repo, url, path, NULL));
+          }
+          git_libgit2_shutdown();
+    }
+}
+

Compiling and running the libgit2 sample

+

+# file run.sh
+
+java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+    -Djava.library.path=${LIBGIT2_HOME}/build/ \
+    GitClone.java $*
+

Cloning a github repo using the above run.sh command

+

+sh run.sh https://github.com/libgit2/libgit2.git libgit2
+

Using sqlite3 library from Java (Mac OS)

+

jextract sqlite3.h

+

+jextract \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+  /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sqlite3.h \
+  -t org.sqlite -lsqlite3
+

Java program that uses sqlite3

+

+import jdk.incubator.foreign.MemoryAddress;
+import jdk.incubator.foreign.MemoryAccess;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import org.sqlite.*;
+import static jdk.incubator.foreign.MemoryAddress.NULL;
+import static org.sqlite.sqlite3_h.*;
+import static jdk.incubator.foreign.CLinker.*;
+
+public class SqliteMain {
+   public static void main(String[] args) throws Exception {
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var allocator = SegmentAllocator.ofScope(scope);
+            // char** errMsgPtrPtr;
+            var errMsgPtrPtr = allocator.allocate(C_POINTER);
+
+            // sqlite3** dbPtrPtr;
+            var dbPtrPtr = allocator.allocate(C_POINTER);
+
+            int rc = sqlite3_open(toCString("employee.db",scope), dbPtrPtr);
+            if (rc != 0) {
+                System.err.println("sqlite3_open failed: " + rc);
+                return;
+            } else {
+                System.out.println("employee db opened");
+            }
+
+            // sqlite3* dbPtr;
+            var dbPtr = MemoryAccess.getAddress(dbPtrPtr);
+
+            // create a new table
+            var sql = toCString(
+                "CREATE TABLE EMPLOYEE ("  +
+                "  ID INT PRIMARY KEY NOT NULL," +
+                "  NAME TEXT NOT NULL,"    +
+                "  SALARY REAL NOT NULL )", scope);
+
+            rc = sqlite3_exec(dbPtr, sql, NULL, NULL, errMsgPtrPtr);
+
+            if (rc != 0) {
+                System.err.println("sqlite3_exec failed: " + rc);
+                System.err.println("SQL error: " + toJavaString(MemoryAccess.getAddress(errMsgPtrPtr)));
+                sqlite3_free(MemoryAccess.getAddress(errMsgPtrPtr));
+            } else {
+                System.out.println("employee table created");
+            }
+
+            // insert two rows
+            sql = toCString(
+                "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " +
+                    "VALUES (134, 'Xyz', 200000.0); " +
+                "INSERT INTO EMPLOYEE (ID,NAME,SALARY) " +
+                    "VALUES (333, 'Abc', 100000.0);", scope
+            );
+            rc = sqlite3_exec(dbPtr, sql, NULL, NULL, errMsgPtrPtr);
+
+            if (rc != 0) {
+                System.err.println("sqlite3_exec failed: " + rc);
+                System.err.println("SQL error: " + toJavaString(MemoryAccess.getAddress(errMsgPtrPtr)));
+                sqlite3_free(MemoryAccess.getAddress(errMsgPtrPtr));
+            } else {
+                System.out.println("rows inserted");
+            }
+
+            int[] rowNum = new int[1];
+            // callback to print rows from SELECT query
+            var callback = sqlite3_exec$callback.allocate((a, argc, argv, columnNames) -> {
+                System.out.println("Row num: " + rowNum[0]++);
+                System.out.println("numColumns = " + argc);
+                var argv_seg = argv.asSegment(C_POINTER.byteSize() * argc, scope);
+                var columnNames_seg = columnNames.asSegment(C_POINTER.byteSize() * argc, scope);
+                for (int i = 0; i < argc; i++) {
+                     String name = toJavaString(MemoryAccess.getAddressAtIndex(columnNames_seg, i));
+                     String value = toJavaString(MemoryAccess.getAddressAtIndex(argv_seg, i));
+                     System.out.printf("%s = %s\n", name, value);
+                }
+                return 0;
+            }, scope);
+
+            // select query
+            sql = toCString("SELECT * FROM EMPLOYEE", scope);
+            rc = sqlite3_exec(dbPtr, sql, callback, NULL, errMsgPtrPtr);
+
+            if (rc != 0) {
+                System.err.println("sqlite3_exec failed: " + rc);
+                System.err.println("SQL error: " + toJavaString(MemoryAccess.getAddress(errMsgPtrPtr)));
+                sqlite3_free(MemoryAccess.getAddress(errMsgPtrPtr));
+            } else {
+                System.out.println("done");
+            }
+
+            sqlite3_close(dbPtr);
+        }
+    }
+}
+
+

Compiling and running the sqlite3 sample

+

+java --enable-native-access=ALL-UNNAMED \
+   --add-modules jdk.incubator.foreign \
+   -Djava.library.path=/usr/lib SqliteMain.java
+

Using OpenGL library from Java (Mac OS)

+

jextract glut.h

+

+jextract -t opengl -lGL -l/System/Library/Frameworks/GLUT.framework/Versions/Current/GLUT \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \
+  -C-F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks \
+  /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/GLUT.framework/Headers/glut.h
+

Java program that uses OpenGL

+

+import jdk.incubator.foreign.CLinker;
+import jdk.incubator.foreign.ResourceScope;
+import jdk.incubator.foreign.SegmentAllocator;
+import opengl.*;
+import static jdk.incubator.foreign.CLinker.*;
+import static opengl.glut_h.*;
+
+public class Teapot {
+    private float rot = 0;
+
+    Teapot(SegmentAllocator allocator) {
+        // Reset Background
+        glClearColor(0f, 0f, 0f, 0f);
+        // Setup Lighting
+        glShadeModel(GL_SMOOTH());
+        var pos = allocator.allocateArray(C_FLOAT, new float[] {0.0f, 15.0f, -15.0f, 0});
+        glLightfv(GL_LIGHT0(), GL_POSITION(), pos);
+        var spec = allocator.allocateArray(C_FLOAT, new float[] {1, 1, 1, 0});
+        glLightfv(GL_LIGHT0(), GL_AMBIENT(), spec);
+        glLightfv(GL_LIGHT0(), GL_DIFFUSE(), spec);
+        glLightfv(GL_LIGHT0(), GL_SPECULAR(), spec);
+        var shini = allocator.allocate(C_FLOAT, 113);
+        glMaterialfv(GL_FRONT(), GL_SHININESS(), shini);
+        glEnable(GL_LIGHTING());
+        glEnable(GL_LIGHT0());
+        glEnable(GL_DEPTH_TEST());
+    }
+
+    void display() {
+        glClear(GL_COLOR_BUFFER_BIT() | GL_DEPTH_BUFFER_BIT());
+        glPushMatrix();
+        glRotatef(-20f, 1f, 1f, 0f);
+        glRotatef(rot, 0f, 1f, 0f);
+        glutSolidTeapot(0.5d);
+        glPopMatrix();
+        glutSwapBuffers();
+    }
+
+    void onIdle() {
+        rot += 0.1;
+        glutPostRedisplay();
+    }
+
+    public static void main(String[] args) {
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var allocator = SegmentAllocator.ofScope(scope);
+            var argc = allocator.allocate(C_INT, 0);
+            glutInit(argc, argc);
+            glutInitDisplayMode(GLUT_DOUBLE() | GLUT_RGB() | GLUT_DEPTH());
+            glutInitWindowSize(500, 500);
+            glutCreateWindow(CLinker.toCString("Hello Panama!", scope));
+            var teapot = new Teapot(allocator);
+            var displayStub = glutDisplayFunc$func.allocate(teapot::display, scope);
+            var idleStub = glutIdleFunc$func.allocate(teapot::onIdle, scope);
+            glutDisplayFunc(displayStub);
+            glutIdleFunc(idleStub);
+            glutMainLoop();
+        }
+    }
+}
+

Compiling and running the OpenGL sample

+

+java -XstartOnFirstThread --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+    -Djava.library.path=.:/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/ Teapot.java $*
+

Using tensorflow (Mac OS)

+

getting libtensorflow

+ +

jextract c_api.h

+

+jextract --source \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \
+  -t org.tensorflow \
+  -I ${LIBTENSORFLOW_HOME}/include \
+  -l ${LIBTENSORFLOW_HOME}/lib/libtensorflow.dylib \
+  ${LIBTENSORFLOW_HOME}/include/tensorflow/c/c_api.h
+
+javac --add-modules jdk.incubator.foreign org/tensorflow/*.java
+

Python program that creates and saves model

+

The following Python program should be run to create and save model which will read and printed by a Java program.

+

Note: you need to install tensorflow package to run this python script.

+

+import tensorflow as tf
+from tensorflow.keras import models, layers
+from tensorflow.keras.datasets import mnist
+
+model = tf.keras.models.Sequential([
+  tf.keras.layers.Flatten(input_shape=(28, 28)),
+  tf.keras.layers.Dense(128,activation='relu'),
+  tf.keras.layers.Dense(10, activation='softmax')
+])
+
+model.compile(
+    loss='sparse_categorical_crossentropy',
+    optimizer=tf.keras.optimizers.Adam(0.001),
+    metrics=['accuracy'],
+)
+
+print(model.summary())
+
+(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
+
+train_images = train_images/255.0
+test_images = test_images/255.0
+
+model.fit(train_images, train_labels,
+    epochs=4, batch_size=128, verbose=1)
+
+test_loss, test_accuracy = model.evaluate(test_images, test_labels)
+
+print(test_loss, test_accuracy)
+
+model.save("saved_mnist_model")
+

Java program that uses Tensorflow C API

+

+import jdk.incubator.foreign.*;
+import static jdk.incubator.foreign.CLinker.*;
+import static jdk.incubator.foreign.MemoryAccess.*;
+import static jdk.incubator.foreign.MemoryAddress.*;
+import static org.tensorflow.c_api_h.*;
+import org.tensorflow.*;
+
+// simple program that loads saved model and prints basic info on operations in it
+
+public class TensorflowLoadSavedModel {
+    public static void main(String... args) throws Exception {
+        System.out.println("TensorFlow C library version: " + toJavaString(TF_Version()));
+
+        if (args.length == 0) {
+            System.err.println("java TensorflowLoadSavedModel <saved model dir>");
+            System.exit(1);
+        }
+
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var allocator = SegmentAllocator.ofScope(scope);
+            var graph = TF_NewGraph();
+            var status = TF_NewStatus();
+            var sessionOpts = TF_NewSessionOptions();
+
+            var savedModelDir = toCString(args[0], scope);
+            var tags = allocator.allocate(C_POINTER, toCString("serve", scope));
+            var session = TF_LoadSessionFromSavedModel(sessionOpts, NULL, savedModelDir, tags, 1, graph, NULL, status);
+
+            if (TF_GetCode(status) != TF_OK()) {
+                System.err.printf("cannot load session from saved model: %s\n",
+                    toJavaString(TF_Message(status)));
+            } else {
+                System.err.println("load session from saved model works!");
+            }
+
+            // print operations
+            var size = allocator.allocate(C_LONG_LONG);
+            var operation = NULL;
+            while (!(operation = TF_GraphNextOperation(graph, size)).equals(NULL)) {
+                System.out.printf("%s : %s\n",
+                    toJavaString(TF_OperationName(operation)),
+                    toJavaString(TF_OperationOpType(operation)));
+            }
+
+            TF_DeleteGraph(graph);
+            TF_DeleteSession(session, status);
+            TF_DeleteSessionOptions(sessionOpts);
+            TF_DeleteStatus(status);
+        }
+    }
+}
+

Compiling and running the Java Tensorflow sample

+

+java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign \
+   TensorflowLoadSavedModel.java saved_mnist_model
+

Using time.h (Mac OS)

+

jextract time.h

+

+jextract -t org.unix \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include \
+   /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/time.h
+

Java program that uses POSIX time library

+

+import static org.unix.time_h.*;
+import static jdk.incubator.foreign.CLinker.*;
+import jdk.incubator.foreign.*;
+import org.unix.*;
+
+public class PanamaTime {
+    public static void main(String[] args) {
+        try (var scope = ResourceScope.newConfinedScope()) {
+            var allocator = SegmentAllocator.ofScope(scope);
+            var now = allocator.allocate(C_LONG, System.currentTimeMillis() / 1000);
+            MemorySegment time = tm.allocate(scope);
+            localtime_r(now, time);
+            System.err.printf("Time = %d:%d\n", tm.tm_hour$get(time), tm.tm_min$get(time));
+        }
+    }
+}
+

Compiling and running the time sample

+

+java --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.foreign PanamaTime.java
+

Using libclang library (Mac OS)

+

jextract Index.h

+

+# LIBCLANG_HOME is the directory where you've installed llvm 9.x or above
+
+jextract --source -t org.llvm.clang -lclang \
+  -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ \
+  -I ${LIBCLANG_HOME}/include/ \
+  -I ${LIBCLANG_HOME}/include/clang-c \
+  ${LIBCLANG_HOME}/include/clang-c/Index.h
+javac --add-modules jdk.incubator.foreign org/llvm/clang/*.java
+

Java program that uses libclang to print AST of a given C program

+

+import jdk.incubator.foreign.*;
+import static jdk.incubator.foreign.CLinker.*;
+import static jdk.incubator.foreign.MemoryAddress.NULL;
+import static org.llvm.clang.Index_h.*;
+import org.llvm.clang.*;
+
+public class ASTPrinter {
+    private static String asJavaString(MemorySegment clangStr) {
+        String str = toJavaString(clang_getCString(clangStr));
+        clang_disposeString(clangStr);
+        return str;
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            System.err.println("java ASTPrinter <C source or header>");
+            System.exit(1);
+        }
+
+        try (var scope = ResourceScope.newConfinedScope()) {
+            // parse the C header/source passed from the command line
+            var index = clang_createIndex(0, 0);
+            var tu = clang_parseTranslationUnit(index, toCString(args[0], scope),
+                    NULL, 0, NULL, 0, CXTranslationUnit_None());
+            // array trick to update within lambda
+            var level = new int[1];
+            var visitor = new MemoryAddress[1];
+
+            // clang Cursor visitor callback
+            visitor[0] = CXCursorVisitor.allocate((cursor, parent, data) -> {
+                var kind = clang_getCursorKind(cursor);
+                var name = asJavaString(clang_getCursorSpelling(scope, cursor));
+                var kindName = asJavaString(clang_getCursorKindSpelling(scope, kind));
+                System.out.printf("%s %s %s", " ".repeat(level[0]), kindName, name);
+                var type = clang_getCursorType(scope, cursor);
+                if (CXType.kind$get(type) != CXType_Invalid()) {
+                    var typeName = asJavaString(clang_getTypeSpelling(scope, type));
+                    System.out.printf(" <%s>", typeName);
+                }
+                System.out.println();
+
+                // visit children
+                level[0]++;
+                clang_visitChildren(cursor, visitor[0], NULL);
+                level[0]--;
+
+                return CXChildVisit_Continue();
+            });
+
+            // get the AST root and visit it
+            var root = clang_getTranslationUnitCursor(scope, tu);
+            clang_visitChildren(root, visitor[0], NULL);
+
+            clang_disposeTranslationUnit(tu);
+            clang_disposeIndex(index);
+        }
+    }
+}
+

Compiling and running the libclang sample

+

+java --enable-native-access=ALL-UNNAMED \
+    -Djava.library.path=${LIBCLANG_HOME}/lib \
+    --add-modules jdk.incubator.foreign \
+    ASTPrinter.java $*
+ +