Essayez d'appeler Nim depuis Java via JNI

Aperçu

Je voulais vraiment utiliser les chevilles de Nim (PEG) de Java, j'ai donc étudié comment appeler les fonctions Nim via JNI.

Des faits étonnants se sont avérés ...

Avec jnim, il semble que vous puissiez créer une bibliothèque partagée avec uniquement des fichiers Nim. Après demain, j'organiserai un peu plus la procédure.

jnim_call.nim



import jnim
import dynlib

#Renvoie l'argument tel quel
proc Java_HelloJNI_hello*(env: JNIEnvPtr, me:jobject, arg:jstring ): jstring {.cdecl,exportc,dynlib.} = 
  return arg

Compiler et exécuter

$ nim c --app:lib --noMain -o:libHelloJNI.so  jnim_call.nim
$ java -Djava.library.path=. HelloJNI  "Peux-tu m'appeler"
Peux-tu m'appeler

~~ Donc, ce qui suit est un article poubelle. ~~

Dans la source Nim ci-dessus, lorsque j'ai converti l'argument de la méthode JNI en jstring-> cstring-> string, pour une raison quelconque, la JVM a craché un vidage de mémoire et s'est terminée anormalement.

Dans la version initiale, cela ne fonctionnait que si j'appelais d'abord NimMain et que je l'initialisais, il semble donc que ce soit la cause.

J'ai pensé que je devrais appeler NimMain moi-même, et quand j'ai écrit ce qui suit en utilisant le pragma emit, j'ai pu l'appeler avec succès sans cracher un vidage de mémoire! !!

jnim_call.nim



import jnim
import dynlib

#Renvoie l'argument tel quel
proc Java_HelloJNI_hello*(env: JNIEnvPtr, me:jobject, arg:jstring ): jstring {.cdecl,exportc,dynlib.} = 
  #Initialiser Nim
  {.emit: """
  NimMain();
  """.}

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

  #Ouvert à la fin
  defer:
    env.ReleaseStringUTFChars(env,arg,argNim)

  # cstring ->conversion de chaîne
  var ss = "Hello " & $argNim

  #Générer une chaîne et quitter
  return env.NewStringUTF(env,ss)

Vous pouvez maintenant appeler peg depuis Java.

※※※※ Ajout encore plus ※※※※

Quand j'ai appelé la méthode JNI environ 10000 fois en boucle pour voir si je devais appeler NimMain à chaque fois, Il s'est terminé anormalement avec l'erreur "impossible d'enregistrer la variable globale; trop de variables globales". Il semble que je n'ai besoin d'appeler NimMain qu'une seule fois la première fois, j'ai donc décidé d'utiliser les variables globales définies dans jnim. Avec ce support, même si vous appelez la méthode JNI par Nim 100 000 fois, aucune erreur ne se produira! !! !!

jnim_call.nim



import jnim
import dynlib

#Renvoie l'argument tel quel
proc Java_HelloJNI_hello*(env: JNIEnvPtr, me:jobject, arg:jstring ): jstring {.cdecl,exportc,dynlib.} = 

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

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

  #Ouvert à la fin
  defer:
    env.ReleaseStringUTFChars(env,arg,argNim)

  # cstring ->conversion de chaîne
  var ss = "Hello " & $argNim

  #Générer une chaîne et quitter
  return env.NewStringUTF(env,ss)

Dans le [Forum] de Nim (http://forum.nim-lang.org/t/2696/1), il y avait un message indiquant qu'une erreur de segmentation s'est produite lors d'un appel natif avec jnim. Le même phénomène s'est produit avec la méthode native que j'ai créée dans mon article. En guise de solution, j'ai pu contourner le problème en passant -d: noSignalHandler comme option du compilateur.

Décrit les options de compilation pour créer un fichier SO natif (DLL) avec jnim.

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

Java->Nim J'ai lu [cet article] de @ shsnow23 (http://qiita.com/shsnow23/items/1696687cca61bfedb1cc) et j'ai senti que je pouvais appeler Java-> Nim, donc j'ai fait un échantillon.

JNI, pas JNA

Compte tenu de la libération de mémoire après le retour d'une chaîne de caractères de Nim, j'ai pensé qu'il serait plus sûr d'utiliser la création d'objets Java dans la méthode JNI, je l'ai donc implémentée dans JNI.

procédure

Côté Java

HelloJNI.java


//Classe de test JNI
public class HelloJNI {
    //Charger la bibliothèque
    static {
        System.loadLibrary("HelloJNI");
    }
    //Méthode JNI
    private native String hello(String str);

    //Fonction principale pour les tests
    public static void main(String[] args){
        HelloJNI h = new HelloJNI();
        System.out.println(h.hello(args[0]));
    }
}

Côté Nim

proc hello_nim(s:cstring) : cstring  {.exportc.} = 
    #Retour par concaténation de chaînes de caractères
    var ss:string = ""
    ss = "hello " & $s
    return ss

Côté C

L'en-tête est automatiquement généré par javah, il n'est donc pas nécessaire de le modifier.

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

Source C (JNI). Convertit les arguments passés en type natif avant d'appeler la fonction nim. Si vous n'appelez pas la fonction appelée NimMain, l'initialisation côté Nim ne sera pas effectuée, ce qui entraînera une erreur d'exécution. Pour savoir comment mettre en œuvre JNI, je me suis référé à ici.

HelloJNI.c


#include "HelloJNI.h"

//Définition de la fonction côté Nim
#include "nimcache/hello.h"

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

  //Argument char*Conversion en
  NCSTRING argNim = (*env)->GetStringUTFChars(env,arg,NULL);

  //Appeler les fonctions Nim
  NCSTRING ret = hello_nim(argNim);

  //Libérez la mémoire réservée
  (*env)->ReleaseStringUTFChars(env,arg,argNim);

  //Construisez une chaîne de caractères à partir de la valeur de retour de Nim et renvoyez-la
  return (*env)->NewStringUTF(env,ret);
}

Créer un script

Je ne peux pas faire un makefile parce que je suis si faible ... NIM_HOME est le répertoire dans lequel Nim est installé.

build.sh



sharedlib=libHelloJNI.so

#Supprimer le produit précédent pour le moment
rm -rf nimcache
rm ${sharedlib}

#Compilation Nim et sortie source C La source est sortie vers nimcache
nim c --noMain --noLinking --header:hello.h Hello.nim

#Nimcache avec source JNI C/*.c compile également
gcc -shared -fPIC \
  -I ${JAVA_HOME}/include \
  -I ${JAVA_HOME}/include/linux \
  -I ${NIM_HOME}/lib \
  -o ${sharedlib} \
  HelloJNI.c nimcache/*.c

#Java est également compilé
javac HelloJNI.java
#Essai
java -Djava.library.path=. HelloJNI

État du fichier après compilation

Ce sera dans un tel état.

.
├── 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

Ensuite, le résultat de l'exécution

Iyahoooooooooooo

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

Recommended Posts

Essayez d'appeler Nim depuis Java via JNI
Essayez d'appeler des méthodes synchronisées à partir de plusieurs threads en Java
Essayez d'appeler JavaScript en Java
Appeler la bibliothèque Java à partir de C avec JNI
Appeler java depuis C ++ sur Android NDK
Essayez d'appeler le service CORBA sur Java 11+
Essayez d'exécuter Kubernetes Job à partir de Java
Essayez d'appeler Watson NLU qui semble prendre en charge le japonais à partir du SDK Java
Utiliser les bibliothèques natives de Scala via Java CPP + Java
Essayez Java 8 Stream
Essayez d'accéder à l'ensemble de données depuis Java en utilisant JZOS
Essayez grossièrement Java 9
J'ai essayé d'appeler du code natif Java / Objective-C depuis Flutter
Verrou pessimiste sur NW ABAP depuis Java via JCo
Changements de Java 8 à Java 11
Somme de Java_1 à 100
Évaluer la source Java à partir de Java
Accédez à API.AI depuis Java
De Java à Ruby !!
Essayez la valeur de retour Java