Создание файлов заголовков JNI для файлов классов в JDK 10

Неотъемлемой частью Java Native Interface (JNI) является соединение кода JVM и собственного кода через заголовки C. Раньше способ создания этих файлов заголовков был довольно простым: просто вызывайте утилиту командной строки javah для файлов классов. Затем этот процесс будет генерировать прототипы для любого метода, отмеченного модификатором native.

Однако, начиная с Java 10, утилита javah была удалена и предложена ее замена — это новый флаг «-h» для javac. Замена работает нормально, если доступны исходные файлы Java, однако не работает в случаях, когда доступны только скомпилированные файлы классов. (Проблема, вызвавшая этот вопрос, заключается в том, что я пытаюсь создать привязки JNI из источников Scala. Мой текущий подход состоял в том, чтобы сначала скомпилировать их, а затем запустить javah для полученных файлов классов.)

В ситуации, когда доступны только скомпилированные файлы классов, есть ли способ сгенерировать заголовочные файлы C, подобно тому, как это делал javah?


person Jakob Odersky    schedule 27.03.2018    source источник
comment
Может быть просто гипотетически, но компилировать исходники scala в jar и затем использовать jar в classpath для флага javac -h не вариант?   -  person Naman    schedule 27.03.2018
comment
Существует уродливое решение, основанное на javap. Вы можете декомпилировать класс, оставить только нативные вещи и использовать javac для обратной компиляции. Но это довольно некрасивый подход.   -  person Oo.oO    schedule 27.03.2018
comment
@nullpointer, к сожалению, предложенное вами решение не работает. javac действительно требует собственных модификаторов в исходном коде. В настоящее время ответ mko выглядит наиболее многообещающим.   -  person Jakob Odersky    schedule 27.03.2018
comment
Аналогичный вопрос, касающийся использования javah для Kotlin: 48816188/   -  person Jakob Odersky    schedule 11.04.2018


Ответы (3)


Мы можем использовать gjavah для создания файлов заголовков JNI.

person Glavo    schedule 11.04.2018

Вы всегда можете пройти через javap. Знаю, знаю. Это уродливо, имеет много предположений, но в случае, если вам отчаянно нужно генерировать заголовки для большого количества файлов, это может быть единственным вариантом.

#!/bin/bash

# FIRST_ARG - full class name (with package)
# SECOND_ARG - class path

CLASS_NAME=`javap -cp $2 $1 | \
  grep -v "Compiled from" | \
  grep "public class" | \
  cut -f3 -d" " | \
  awk -F"." '{ print $NF }'`

PACKAGE_NAME=`javap -cp $2 $1 | \
  grep -v "Compiled from" | \
  grep "public class" | \
  cut -f3 -d" " | \
  sed s/\.${CLASS_NAME}$//`

DIR_NAME=`echo $PACKAGE_NAME | sed 's|\.|/|g'`
mkdir -p java_jni/${DIR_NAME}

JAVA_FILE_NAME="java_jni/${DIR_NAME}/${CLASS_NAME}.java"

echo "package ${PACKAGE_NAME};" > ${JAVA_FILE_NAME}
echo "public class ${CLASS_NAME} {" >> ${JAVA_FILE_NAME}

javap -cp $2 $1 | grep "native" | while read line; do
  param=0
  comma=`echo $line | grep "," | wc -l`
  while [ $comma -gt 0 ]; do
    line=`echo $line | sed "s/,/ param_${param}|/"`
    let param=param+1
    comma=`echo $line | grep "," | wc -l`
  done
  line=`echo $line | sed "s/)/ param_${param})/" | sed 's/|/,/g'`
  echo "  $line" >> ${JAVA_FILE_NAME}
done

echo "}" >> ${JAVA_FILE_NAME}

mkdir -p c_header
javac -h c_header ${JAVA_FILE_NAME}

Спорим, его можно сделать намного красивее.

Для меня сейчас, когда я медленно начинаю думать о неизбежном движении к Java 10 и обо всех этих случаях, когда меня может удивить несуществующий исходный код Java, я думаю, что неплохо иметь в своем распоряжении какой-нибудь инструмент. На всякий случай.

person Oo.oO    schedule 27.03.2018
comment
Это не работает, если собственные методы принимают параметры (javac жалуется на ошибку: ожидается «идентификатор») - person Salomon BRYS; 03.06.2019
comment
Спасибо, что заметили @SalomonBRYS !! - person Oo.oO; 03.06.2019

Думаю, лучшее решение - просто установить jdk8. И нет необходимости удалять jdk10, просто измените переменную среды.

person W. X    schedule 26.06.2018