Versuchen Sie, Nim von Java über JNI aufzurufen

Überblick

Ich wollte unbedingt Nims Pegs (PEGs) aus Java verwenden, also habe ich untersucht, wie man Nim-Funktionen über JNI aufruft.

Es stellten sich erstaunliche Fakten heraus ...

Mit jnim können Sie anscheinend eine gemeinsam genutzte Bibliothek nur mit Nim-Dateien erstellen. Nach morgen werde ich das Verfahren etwas weiter organisieren.

jnim_call.nim



import jnim
import dynlib

#Geben Sie das Argument so zurück, wie es ist
proc Java_HelloJNI_hello*(env: JNIEnvPtr, me:jobject, arg:jstring ): jstring {.cdecl,exportc,dynlib.} = 
  return arg

Kompilieren und ausführen

$ nim c --app:lib --noMain -o:libHelloJNI.so  jnim_call.nim
$ java -Djava.library.path=. HelloJNI  "Kannst du mich anrufen"
Kannst du mich anrufen

~~ Das Folgende ist also ein Müllartikel. ~~

Als ich in der obigen Nim-Quelle das Argument der JNI-Methode in jstring-> cstring-> string konvertierte, spuckte die JVM aus irgendeinem Grund einen Core-Dump aus und wurde abnormal beendet.

In der ersten Version hat es nicht funktioniert, es sei denn, ich habe zuerst NimMain aufgerufen und es initialisiert. Es scheint also, dass dies die Ursache ist.

Ich dachte, ich sollte NimMain alleine aufrufen, und als ich das Folgende mit dem Emit-Pragma schrieb, konnte ich es erfolgreich aufrufen, ohne einen Core-Dump auszuspucken! !!

jnim_call.nim



import jnim
import dynlib

#Geben Sie das Argument so zurück, wie es ist
proc Java_HelloJNI_hello*(env: JNIEnvPtr, me:jobject, arg:jstring ): jstring {.cdecl,exportc,dynlib.} = 
  #Initialisiere Nim
  {.emit: """
  NimMain();
  """.}

  #Argument String->cstring-Konvertierung
  var argNim:cstring = env.GetStringUTFChars(env,arg,nil)

  #Am Ende öffnen
  defer:
    env.ReleaseStringUTFChars(env,arg,argNim)

  # cstring ->String-Konvertierung
  var ss = "Hello " & $argNim

  #Generieren Sie einen String und beenden Sie ihn
  return env.NewStringUTF(env,ss)

Jetzt können Sie peg von Java aus aufrufen.

※※※※ Zugabe noch mehr ※※※※

Als ich die JNI-Methode ungefähr 10.000 Mal in einer Schleife aufrief, um zu sehen, ob ich jedes Mal NimMain aufrufen musste, Es wurde abnormal mit dem Fehler "Globale Variable kann nicht registriert werden; zu viele globale Variablen" beendet. Es scheint, dass ich NimMain nur einmal beim ersten Mal aufrufen muss, also habe ich mich entschieden, die in jnim definierten globalen Variablen zu verwenden. Mit dieser Unterstützung tritt auch dann kein Fehler auf, wenn Sie die JNI-Methode 100.000 Mal von Nim aufrufen! !! !!

jnim_call.nim



import jnim
import dynlib

#Geben Sie das Argument so zurück, wie es ist
proc Java_HelloJNI_hello*(env: JNIEnvPtr, me:jobject, arg:jstring ): jstring {.cdecl,exportc,dynlib.} = 

  #Initialisiere Nim
  if theEnv == nil :
    {.emit: """
    NimMain();
    """.}
    theEnv = env

  #Argument String->cstring-Konvertierung
  var argNim:cstring = env.GetStringUTFChars(env,arg,nil)

  #Am Ende öffnen
  defer:
    env.ReleaseStringUTFChars(env,arg,argNim)

  # cstring ->String-Konvertierung
  var ss = "Hello " & $argNim

  #Generieren Sie einen String und beenden Sie ihn
  return env.NewStringUTF(env,ss)

In Nims Forum gab es einen Beitrag, in dem ein Segmentierungsfehler bei einem nativen Aufruf mit jnim aufgetreten ist. Das gleiche Phänomen trat bei der nativen Methode auf, die ich in meinem Artikel erstellt habe. Als Lösung konnte ich das Problem umgehen, indem ich -d: noSignalHandler als Compileroption übergab.

Beschreibt die Kompilierungsoptionen zum Erstellen einer nativen SO-Datei (DLL) mit jnim.

nim c --out:libHelloJNI.so \
      --app:lib \
      --nomain \
      --threads:on \
      -d:release \
      -d:noSignalHandler \
      hello.nim

Java->Nim Ich las @ shsnow23s diesen Artikel und hatte das Gefühl, Java-> Nim aufrufen zu können, also machte ich ein Beispiel.

JNI, nicht JNA

In Anbetracht der Speicherfreigabe nach der Rückgabe einer Zeichenfolge von Nim hielt ich es für sicherer, die Java-Objekterstellung in der JNI-Methode zu verwenden, und implementierte sie daher in JNI.

Verfahren

Java-Seite

HelloJNI.java


//JNI-Testklasse
public class HelloJNI {
    //Bibliothek laden
    static {
        System.loadLibrary("HelloJNI");
    }
    //JNI-Methode
    private native String hello(String str);

    //Hauptfunktion zum Testen
    public static void main(String[] args){
        HelloJNI h = new HelloJNI();
        System.out.println(h.hello(args[0]));
    }
}

Nim Seite

proc hello_nim(s:cstring) : cstring  {.exportc.} = 
    #Rückkehr durch Verketten von Zeichenketten
    var ss:string = ""
    ss = "hello " & $s
    return ss

C-Seite

Der Header wird automatisch von javah generiert, sodass er nicht geändert werden muss.

HelloJNI.h


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */

#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloJNI
 * Method:    hello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_HelloJNI_hello
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

C (JNI) Quelle. Konvertieren Sie die übergebenen Argumente in den nativen Typ, bevor Sie die Funktion nim aufrufen. Wenn Sie die Funktion NimMain nicht aufrufen, wird die Initialisierung auf der Nim-Seite nicht ausgeführt, was zu einem Laufzeitfehler führt. Informationen zur Implementierung von JNI finden Sie unter hier.

HelloJNI.c


#include "HelloJNI.h"

//Funktionsdefinition auf der Nim-Seite
#include "nimcache/hello.h"

JNIEXPORT jstring JNICALL Java_HelloJNI_hello
  (JNIEnv * env, jobject pThis, jstring arg) 
{
  //Anruf erforderlich
  NimMain();

  //Char Argument*Umstellung auf
  NCSTRING argNim = (*env)->GetStringUTFChars(env,arg,NULL);

  //Nim-Funktionen aufrufen
  NCSTRING ret = hello_nim(argNim);

  //Geben Sie den reservierten Speicher frei
  (*env)->ReleaseStringUTFChars(env,arg,argNim);

  //Konstruiert eine Zeichenfolge aus dem Rückgabewert von Nim und gibt sie zurück
  return (*env)->NewStringUTF(env,ret);
}

Skript erstellen

Ich kann kein Makefile machen, weil ich so schwach bin ... NIM_HOME ist das Verzeichnis, in dem Nim installiert ist.

build.sh



sharedlib=libHelloJNI.so

#Löschen Sie das vorherige Produkt vorerst
rm -rf nimcache
rm ${sharedlib}

#Nim-Kompilierung & C-Quellausgabe Die Quelle wird in Nimcache ausgegeben
nim c --noMain --noLinking --header:hello.h Hello.nim

#Nimcache mit JNI C-Quelle/*.c auch kompilieren
gcc -shared -fPIC \
  -I ${JAVA_HOME}/include \
  -I ${JAVA_HOME}/include/linux \
  -I ${NIM_HOME}/lib \
  -o ${sharedlib} \
  HelloJNI.c nimcache/*.c

#Java wird ebenfalls kompiliert
javac HelloJNI.java
#Testlauf
java -Djava.library.path=. HelloJNI

Dateistatus nach der Kompilierung

Es wird in einem solchen Zustand sein.

.
├── compile.sh
├── HelloJNI.java
├── HelloJNI.class
├── HelloJNI.h
├── HelloJNI.c
├── Hello.nim
├── libHelloJNI.so
└── nimcache
    ├── Hello.c
    ├── Hello.o
    ├── hello.h
    ├── stdlib_system.c
    └── stdlib_system.o

Dann das Ausführungsergebnis

Iyahoooooooooooo

$ java -Djava.library.path=. HelloJNI "Nim Nim Nim"
Hallo Nim Nim Nim

Recommended Posts

Versuchen Sie, Nim von Java über JNI aufzurufen
Versuchen Sie, synchronisierte Methoden aus mehreren Threads in Java aufzurufen
Versuchen Sie, JavaScript in Java aufzurufen
Rufen Sie die Java-Bibliothek von C mit JNI auf
Rufen Sie Java von C ++ auf Android NDK auf
Versuchen Sie, den CORBA-Dienst unter Java 11+ aufzurufen
Versuchen Sie, Kubernetes Job von Java aus auszuführen
Versuchen Sie, Watson NLU, die Japanisch zu unterstützen scheint, vom Java SDK aus aufzurufen
Verwenden Sie native Bibliotheken von Scala über Java CPP + Java
Probieren Sie Java 8 Stream aus
Versuchen Sie, mit JZOS von Java aus auf das Dataset zuzugreifen
Versuchen Sie es mit Java 9
Ich habe versucht, nativen Java / Objective-C-Code von Flutter aus aufzurufen
Pessimistische Sperre für NW ABAP von Java über JCo
Änderungen von Java 8 zu Java 11
Summe von Java_1 bis 100
Eval Java-Quelle von Java
Greifen Sie über Java auf API.AI zu
Von Java zu Ruby !!
Versuchen Sie es mit dem Java-Rückgabewert