К. Арнольд, Д. Гослинг - Язык программирования Java (1160779), страница 23
Текст из файла (страница 23)
Однако код Java,содержащий родные методы, ни в коем случае не может применяться в качестве аплета, запускаемого удаленным пользователем, посколькуаплет должен соответствовать требованиям переносимости и безопасности.При использовании родных методов также приходится жертвовать защитными ограничениями Java. Так, в традиционных языках, можетпроисходить выход за границы массива и появление неопределенных значений указателей. Все ограничения на родные методы задаются толькотем языком, на котором они написаны.В этой главе рассказано, как реализовать родной метод на языке C в системах семейства POSIX (к которым относятся, например, Windows NT ибольшинство реализаций UNIX).
Некоторые детали в вашей системе могут отличаться от описанных, а многие среды поддерживают и другиеязыки кроме C, например, C++. Информацию об этом можно найти в вашей документации. Здесь мы рассматриваем многие важные аспектысогласования языка C с системой Java компании Sun, версия 1.0.2. Возможно, в вашей локальной среде присутствуют изменения иусовершенствования или используется совершенно иная схема согласования.
В частности, описанные здесь способы связывания (binding)родных методов наверняка изменятся в будущих версиях. Но даже с учетом этих обстоятельств, данная глава поможет вам понять некоторыеаспекты связывания родных методов, не зависящие от конкретной схемы, принятой в вашей среде.А.1 ОбзорПри стыковке программ на Java с языком C возникают следующие основные проблемы:Как происходит согласование имен? Полное имя метода в Java имеет вид пакет.класс.метод, однако в C нет ни пакетов, ни классов.Кроме того, согласование усложняется тем, что в идентификаторах Java используется кодировка Unicode, а в идентификаторах С —кодировка ASCII, поэтому необходим дополнительный перевод символов Unicode в символы, разрешенные в C.Как разрешается проблема различных парадигм вызова? Например, каждый нестатический метод в Java располагает ссылкой this,которая, в сущности, является неявным параметром метода.
В C нет ни методов, ни неявных параметров.Как происходит согласование типов? Родная реализация метода должна обращаться к полям объекта this, и, возможно, методам илиполям объектов других типов. Как представить классы Java в языке С?Как происходит согласование ошибок? Java сообщает о них при помощи исключений, но в C исключения отсутствуют.Как происходит согласование средств безопасности? Java следит за выходом за границы массивов и преобразованиями типов, а такжеосуществляет сборку мусора для борьбы с утечкой памяти и “зависшими” указателями. C и C++ не обладают этими возможностями, таккак же производить такие проверки? А что должно происходить в языках типа Pascal, где такая проверка присутствует?Как происходит согласование работы с памятью? Как программа на языке C создает объекты Java?●●●●●●Решая эти и другие проблемы, приходится идти на компромиссы.
Например, C и C++ не обладают средствами безопасности Java в работе смассивами, поэтому при согласовании предполагается, что родные методы C и C++ небезопасны в этом отношении. Хотя такой выход и неидеален, он все же выглядит вполне естественно по отношению к C и C++, для которых скорость считается более важной, чем страховка.Например, чтобы реализовать подобную проверку в C или C++, пришлось бы обращаться ко всем элементам массива посредством проверочныхфункций Java. Такой вариант выглядит неестественно и медленно работает, а поскольку основным доводом в пользу родных методов являетсяскорость, подобный компромисс окажется неверным. К тому же он не будет нормально работать с существующим кодом, в котором используетсястандартная для C и C++ парадигма работы с массивами.А.2 Согласование с C и C++Согласование Java c языком С происходит довольно прямолинейно.
Для стыковки родных методов с вызовами C используется сгенерированныйзаголовочный файл, содержащий все необходимые объявления типов и сигнатуры функций, а также программные “заглушки” на C, которыепомогают runtime-системе Java вызывать эти методы.Мы рассмотрим только основные моменты согласования. Программа, которая используется здесь в качестве примера, представляет собой тексткласса LockableClass из пакета local: /К сожалению, мы не смогли воспользоваться соглашением об именах пакетов, поскольку это привело бы кудлинению идентификаторов и затруднило бы работу с книгой./package local;import java.io.*;class LockableFile extends File {LockableFile(String path) {super(path);}// допустимые параметры lock()public final static int READ = 0,WRITE = 1;public native void lock(int type) throws IOException;public native void unlock() throws IOException;private int fd = -1;static {System.load("LockableFile");}}После того, как эта программа будет обработана компилятором Java, следует сгенерировать заголовочный файл с помощью утилиты javah,передав ей имя класса, для которого создается данный файл.
Утилита сгенерирует файл, содержащий все объявления и определения на языке C,необходимые для стыковки. В нашем примере команда будет выглядеть следующим образом:javah local.LockableFileИмя сгенерированного файла определяется по имени класса, в соответствии с описанной ниже схемой согласования имен. В нашем случаезаголовочный файл будет называться local_LockableFile.h. Содержимое этого заголовочного файла и реализация его родных методов будутприведены ниже в данной главе.Процесс согласования с C++ выглядит так же. Фактически, согласование с C++ сводится к включению сгенерированного заголовочного файла вобъявление extern " C":"C":extern "C" {# include local_LockableFile.h}Символы, которые используются во время выполнения программы для вызова оболочек родных методов, создаются в пространстве имен С, а нетак называемых “преобразованных” (mangled) имен C++, поэтому родные методы должны быть написаны на C.
/На самом деле это не совсемтак —приложив некоторые усилия, опытный программист сможет обойти это ограничение, но для простоты описания и реализации будет лучшесогласиться с ним./ Основное следствие заключается в том, что вы не сможете использовать перегрузку методов C++ для реализации родныхметодов. В сущности, правильнее было бы сказать, что для родных методов прямая стыковка с C++ вообще не используется, однако возможнакосвенная стыковка за счет вызовов функций C в C++. Разумеется, согласование программ на Java с C++ может быть улучшено, и несомненно этобудет сделано в будущих версиях.Реализуя родные методы на C или C++, вы должны связать откомпилированный код с приложением на Java; для этого необходимо создатьбиблиотеку динамического связывания и соединить ее со своей программой.
Чаще всего программист выделяет статический блок, наподобиеприведенного выше в классе LockableFile, а затем вызывает один из двух статических методов класса System, предназначенных для загрузкибиблиотек:public synchronized void load(String pathname)Загружает динамическую библиотеку, расположенную по заданному полному имени pathname. В некоторых случаях полное имямодифицируется в соответствии с требованиями локальной системы. Если файл не обнаружен или не найдены символы,необходимые для работы библиотеки, возбуждается исключение UnsatisfiedLinkError.public synchronized void loadLibrary(String libname)Загружает динамическую библиотеку с указанным именем libname.
Вызов LoadLibrary должен осуществляться в статическоминициализаторе первого загружаемого класса (то есть класса, содержащего вызываемый метод main). Попытки многократнойзагрузки одной и той же библиотеки игнорируются. Если библиотека не найдена, возбуждается исключение UnsatisfiedLinkError.Метод load требует указания полного имени файла, однако во время разработки лучше пользоваться именно этой версией, поскольку онанормально сообщает о неопределенных символах.
Метод loadLibrary переводит любую ошибку, включая неопределенные символы, в ошибку“библиотека не найдена”. Если в библиотеке присутствуют неопределенные символы, то подобный перевод скроет от вас важную информациюоб истинной причине происходящего. Если вы будете пользоваться методом load до того момента, когда родные методы заработают, после чегозамените его методом loadLibrary, то сможете добиться более подробной информации в ходе разработки и переносимости кода после еезавершения.Методы загрузки библиотек класса System представляют собой сокращения для вызова тех же самых методов класса Runtime, представляющеготекущий runtime-контекст.А.2.1 ИменаДля перевода имен методов и полей на язык C используются полные имена, включающие названия пакетов, в которых все точки (.) заменяютсяподчеркиваниями (_). В тех языках, где не поддерживается возможность перегрузки методов (к ним относится C) вы не сможете реализовать вклассе несколько методов с одинаковыми именами, поскольку им будет соответствовать одно и то же имя функции.Аналогично переводятся и имена типов — за тем исключением, что перед именем типа ставится префикс Class.
В программе на C тип дляLockableFile будет называться Classlocal_LockableFile. Для каждого класса также необходим дескриптор (handle), поскольку он используется длявнутреннего представления ссылок. Имя дескриптора совпадает с именем класса, но вместо префикса Class используется префикс H. Такимобразом, тип дескриптора для LockableFile будет называться Hlocal_LockableFile.Символы имен, относящиеся к набору ASCII (символы, меньшие или равные \u007f) переходят в C и в имена пакетов без изменений. Всеостальные символы переводятся в вид _0dddd, где d — цифры, используемые в символьном представлении Java. Например, символ г (код \u00e3)будет представлен идентификатором _000e3.