Практические занятия с Java и Wasm

автор vadim


WebAssembly, или Wasm, предоставляет обобщенный, легкий собственный двоичный формат для любого языка, который может быть скомпилирован в него. После преобразования исходного языка в WebAssembly этот компактный двоичный файл может работать в различных контекстах, от облачных виртуальных машин и рабочих столов до Интернета вещей и мобильных устройств. Одной из самых интересных сред, поддерживаемых Wasm, является веб-браузер. В этой статье мы рассмотрим, как скомпилировать код Java в WebAssembly, затем запустим демонстрационное приложение на веб-сервере и увидим его в действии.

Зачем использовать WebAssembly для Java?

Wasm предоставляет способ выполнения Java на внешнем интерфейсе в веб-браузере, где ваш Java API может быть вызван из JavaScript.

Компиляторы WebAssembly генерируют двоичный файл из исходного кода Java (или из байт-кода), и этот двоичный файл выполняется браузером с использованием возможностей основной операционной системы, поэтому вы получаете производительность на уровне ОС. Код Java превращается в двоичный файл Wasm, который предоставляет хуки, которые вы вызываете из JavaScript.

Wasm доступен во всех основных современных браузерах, поэтому вы можете использовать его прямо сейчас. Он предлагает превосходную производительность для сложных задач на выбранном вами языке. Кодирование видео, графика и другая интенсивная обработка данных — хорошие кандидаты для этой технологии. Некоторые проекты используют Wasm для устранения ограничений производительности блокчейна.

Если вы какое-то время знакомы с Java, вы можете подумать об апплетах Java, когда услышите о Java в браузере. Будьте уверены, Васм — совершенно другое животное. Это современная высокопроизводительная технология, разработанная с нуля как безопасная изолированная среда выполнения. Благодаря портативности, производительности, безопасности и поддержке многоязычных языков Wasm сегодня является важной частью технологического ландшафта.

Java в Wasm: как это работает

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

В настоящее время GraalVM создает поддержку вывода в Wasm с помощью инструмента собственного образа (в качестве целевого двоичного файла в --features флаг). Если предложение будет успешным, этот путь, скорее всего, станет рекомендуемым. В GraalVM уже есть возможность запуска Wasm, и в целом это отличный проект. Как только GraalVM сможет генерировать бинарные файлы Wasm, его стоит изучить.

А пока есть несколько библиотек для обработки компиляции Java-to-Wasm:

  • ЧайВМ
  • CheerpJ
  • JWebAssembly
  • Байткодер

Мы собираемся использовать TeaVM для компиляции Java Wasm. Я спросил создателя TeaVM Алексея Андреева, почему Java отстает от других языков в реализациях Wasm. Он сказал мне: «Вкратце: написать собственный сборщик мусора или обработку исключений несложно. В Wasm сложно реализовать собственный сборщик мусора из-за отсутствия доступа к стеку». WebAssembly имеет ограниченный доступ к стеку по соображениям безопасности. Вместо того, чтобы искать способы безопасного доступа к стеку, как предложил Андреев, Wasm предпочитает встроить сборку мусора в сам Wasm. (См. страницу проекта WebAssembly на GitHub, чтобы следить за ходом сборки мусора в Wasm.)

Компиляция Java в Wasm с помощью TeaVM

TeaVM задуман как легкая библиотека для создания Wasm из Java. Он черпает вдохновение из Google Web Toolkit, но основывается на байт-коде, а не на исходном коде. Из-за этого он может работать с другими языками JVM, такими как Scala. TeaVM дает нам быстрый способ взглянуть на проект Java Wasm с помощью одного из примеров проектов, который мы будем использовать для демонстрации.

Настройка TeaVM и пример проекта

Для использования TeaVM вам потребуется установленный JDK Java 8 или более поздней версии. Мы собираемся собрать последнюю версию TeaVM и установить ее в наш локальный репозиторий, а затем собрать один из примеров проектов, использующих Wasm, чтобы посмотреть, как он выполняет компиляцию Java-to-Wasm при преобразовании браузера.

Вам понадобится установленный Git для клонирования проекта TeaVM: Git clone https://github.com/konsoletyper/teavm.git. На момент написания TeaVM включен 0.8.0-SNAPSHOT.

После проверки проекта вам необходимо собрать саму библиотеку TeaVM и установить ее в локальный репозиторий. Перейдите в корневой каталог и введите: /teavm/gradlew. Это будет использовать автономный исполняемый файл оболочки Gradle для сборки TeaVM и его локальной установки.

Как только это будет сделано, вы можете перейти к /teavm/samples/pi каталог и тип: ../../gradlew war. Это говорит Gradle собрать образец проекта Pi в файл WAR. Как только это будет завершено, появится /teavm/samples/pi/build/libs/pi.war файл, который мы можем развернуть в контейнере сервлетов.

В моем случае для этой демонстрации я использую Ubuntu, поэтому я установил Tomcat как службу (sudo apt-get install tomcat9), а затем скопировал pi.war файл в /webapps каталог (sudo cp /teavm-wasm/target/pi.war /var/lib/tomcat9/webapps/). Далее я использовал sudo systemctl start tomcat9 для запуска Tomcat. Другой способ — с start.sh/.bat сценарий в /bin каталог, который работает, если вы загрузили автономную версию Tomcat.

Обратите внимание, что пример Pi демонстрирует возможности вывода TeaVM как на JavaScript, так и на Wasm. Мы будем придерживаться Wasm.

Демонстрация Pi для Wasm

Теперь приложение должно работать на localhost:8080/pi. Если вы посетите этот URL-адрес в браузере, вы увидите простой пользовательский интерфейс с двумя ссылками: одна для JavaScript и одна для WebAssembly. Нажмите Веб-сборка и вы увидите вид, подобный показанному ниже.

Пользовательский интерфейс демонстрационного приложения Wasm.IDG

Рисунок 1. Интерфейс WebAssembly Pi

Этот интерфейс позволяет вам указать количество цифр для вычисления пи. Для вычислений используется Java, скомпилированная в Wasm, и вызывается код из JavaScript. Давайте посмотрим, как выполняется этот расчет.

Если вы посмотрите на /teavm/samples/pi проект, это стандартный макет Maven/Gradle, который включает в себя исходные коды как Java, так и веб-приложений. Они хранятся в src/main/java и src/main/webapp, соответственно. Посмотрите на единственный класс Java в src/main/java/org/teavm/samples/pi/PiCalculator.java и вы увидите, что это обычный класс Java с именем PiCalculator. Класс определяет основной метод, который принимает один аргумент из args[] как количество цифр для расчета. Он использует внутренний класс с именем PiDigitSpigot для фактического вычисления числа пи и опирается на единую библиотеку Java, java.math.BigInteger. В целом, это типичная Java-программа. На самом деле, если вы идете в /teavm/samples/pi/build/libs/classes/main и введите java org/teavm/samples/pi/PiCalculator 50он с радостью вычислит число пи до 50 цифр в командной строке.

Теперь давайте посмотрим на сторону веб-приложений, в teavm/samples/pi/src/main/webapp. wasm.html это тот, который нас интересует. Большая часть файла представляет собой базовый HTML, но есть и интересный JavaScript, показанный в листинге 1.

Листинг 1. Веб-приложение для вычисления числа пи


<script type="application/javascript">
  let runner = null;
  function init() {
    TeaVM.wasm.load("wasm/pi.wasm", {
      installImports(o, controller) {
        function putwchars(address, count) {
          let instance = controller.instance;
          let memory = new Int8Array(instance.exports.memory.buffer);
          let string = "";
            for (let i = 0; i < count; ++i) {
              string += $rt_putStdoutCustom(memory[address++]);
            }
          }
          o.teavm.putwcharsOut = putwchars;
          o.teavm.putwcharsErr = putwchars;
        },
      }).then(teavm => {
        this.instance = teavm.instance;
        runner = n => teavm.main([n.toString()]);
        document.getElementById("run").disabled = false;
      })
  }
  function calculate() {
    var count = parseInt(document.getElementById("digit-count").value);
    runner(count);
  }
  init();
</script>
</head>
</body>
<div>
  Digit count:
  <input type="text" id="digit-count" value="1000">
  <button onclick="calculate()" id="run" disabled>Run</button>
</div>
<div id="stdout"></div>
</body>

Для начала строка TeaVM.wasm.load("wasm/pi.wasm", { ... начинается импорт pi.wasm файл с сервера. Это оболочка для встроенной функции WebAssembly.instantiate(), которая указывает браузеру загрузить файл Wasm. Объект, передаваемый в качестве второго параметра метода загрузки, настраивается, и .then() call предоставляет функцию обратного вызова для обработки того, что происходит, когда программа WebAssembly готова.

Документация пока отсутствует TeaVM.wasm.loadно вы можете найти исходный код на GitHub.

Что нас больше всего интересует, так это функция обратного вызова, где мы видим строку: runner = n => teavm.main([n.toString()]);. Вот где мы вызываем нашу Java main() в JavaScript, передавая количество цифр для вычисления в качестве аргумента.

Теперь давайте посмотрим, как PiCalculator.java файл сделал свое преобразование в pi.wasm. в /pi/build.gradle.kts файл, подключаемый модуль TeaVM настроен так, чтобы он указывал на файл Java, как показано в листинге 2.

Листинг 2. Преобразование Java в Wasm


teavm {
    js {
        addedToWebApp.set(true)
    }
    wasm {
        addedToWebApp.set(true)
    }
    wasi {
        outputDir.set(File(buildDir, "libs/wasi"))
        relativePathInOutputDir.set("")
    }
    all {
        mainClass.set("org.teavm.samples.pi.PiCalculator")
    }
}

Вы можете видеть, что плагин TeaVM настроен с mainClass.set("org.teavm.samples.pi.PiCalculator")в all поле, потому что оно применяется как к JavaScript, так и к Wasm (то есть проект выводит как версии JavaScript, так и версии Wasm). Если вы посмотрите на build/generated/teavm/wasm/ вы можете увидеть, куда TeaVM сбросил файлы Wasm.

Заключение

Wasm — многообещающая технология, которая позволяет Java работать в веб-браузере с хорошей производительностью. Хотя работа над ним еще не завершена, такие библиотеки, как TeaVM, предоставляют нам упрощенные средства работы с Java и WebAssembly. TeaVM позволяет нам компилировать код Java в компактный двоичный файл, который выполняется в браузере с использованием возможностей основной операционной системы. Wasm — важная часть технологического ландшафта Java, и он все еще находится на относительно ранних стадиях разработки. Интеграция сбора мусора может расширить его потенциал в нескольких ключевых областях. Это определенно стоит посмотреть.

Related Posts

Оставить комментарий