Уважаемые читатели! Хочу поделиться с вами своим open-source проектом, над которым я работаю в своё свободное время уже достаточно давно, TeaVM. TeaVM представляет собой транслятор из байткода Java в JavaScript. Существует несколько попыток создать JVM на JavaScript, одна из самых удачных — Doppio. Однако, кроме академической, никакой ценности они не представляют, так как скорость интерпретации байт-кода оставляет желать лучшего. Более того, для интерпретации байткода необходимо как минимум загрузить этот байткод в браузер, а это вырождается в загрузку десятков мегабайт class
-файлов.
В отличие от них, TeaVM не интерпретирует байткод, а генерирует JavaScript, который выполняет ровно то, что делал бы байткод, будь он запущен в реальной JVM. Проще говоря, TeaVM декомпилирует байткод Java, но не обратно в Java, а в JavaScript. Разумеется, всё это верно до определённых пределов. Во-первых, в JavaScript попросту отсутствуют некоторые вещи, привычные Java-разработчикам, такие как потоки, полноценная поддержка Юникода (например, поддержка классов символов, регулярные выражения), блокирующий ввод-вывод. Во-вторых, это обусловлено требованиями, которые я предъявлял к компилятору. Например, в TeaVM очень ограничена поддержка reflection. Это следствие одного из преимуществ TeaVM — сравнительно небольшой размер генерируемого файла. Нет, TeaVM не генерирует минимально возможный JavaScript, однако и не станет генерировать огромные многомегабайтные скрипты на каждый чих. Reflection делает невозможным какой-либо статический анализ, поэтому было принято решение от него отказаться.
Прежде чем я продолжу, я хочу для начала показать, на что способен TeaVM. Во-первых, он способен в реальном времени симулировать физику. Во-вторых, он ещё способен по этой физике рисовать красивые картинки в Canvas. Можно увидеть, что JavaScript-файлы сравнительно небольшие. Кстати, обсчёт физики я сам не реализовывал, я всего лишь взял имеющуюся библиотеку JBox2D.
TeaVM задумывался как инструмент для разработчика, который был бы пригоден для разработки браузерных приложений на Java, а не как ещё один академический проект. Я считаю, что подобного рода инструмент должен обладать следующими характерными чертами:
- Приемлемая скорость работы. Никто не будет ждать компилятор часами. А ещё лучше — очень быстрая скорость работы, чтобы разработчик не ждал по несколько минут ради того, чтобы посмотреть, сдвинулась ли кнопочка после его правок в коде.
- Быстрый JavaScript.
- Компактный JavaScript. Пользователь будет очень зол на разработчика, увидев, что страница беспричинно тянет за собой мегабайты JavaScript. Точнее, пользователь не будет смотреть на размер, но вот снижение скорости загрузки страницы заметит невооружённым взглядом.
- Хорошая поддержка набора классов из JDK. Java — это не только язык программирования, но и набор классов из стандартной библиотеки, которые Java-разработчики привыкли использовать в своей повседневной работе.
- Поддержка популярных систем сборки. Таковой в мире Java является, прежде всего, Maven, хотя в последнее время популярность набирает Gradle.
- Поддержка популярных IDE: Eclipse, Netbeans, IntelliJ IDEA.
- Возможность отладки кода, «запущенного» в TeaVM. Поверьте, очень неприятно отлаживать сгенерированный JavaScript, я лично пробовал.
- Возможность интегрироваться с имеющимися библиотеками JavaScript и с API браузера.
Всё вышеперечисленное так или иначе реализовано в TeaVM. Правда, я не могу вот так взять и реализовать всё сразу, поэтому из систем сборки пока поддерживается только Maven, а из IDE — Eclipse. В планах сделать поддержку остальных IDE и систем сборки.
Создание проекта
Я постарался сделать создание нового проекта как можно более простым. Для этого нужно всего лишь выполнить следующую команду в консоли:
mvn -DarchetypeCatalog=local
-DarchetypeGroupId=org.teavm
-DarchetypeArtifactId=teavm-maven-webapp
-DarchetypeVersion=0.2.1
archetype:generate
Если вы — пользователь Windows, то вам необходимо будет слегка переписать данную команду. А ещё проще — взять соответствующие реквизиты и использовать их для создания нового Maven-проекта в IDE. Кстати, если ваша IDE — Eclipse, то есть смысл установить плагин из репозитория.
С имеющимся проектом можно поиграться. Если есть вопросы, можно обратиться к документации. А если вы не нашли ответ на свой вопрос в документации, публикуйте его в виде issue в GitHub.
Плагин к Eclipse
Код для production генерируется системой сборки, однако при разработке каждый раз пересобирать проект не слишком удобно. Ведь для этого необходимо каждый раз запускать maven, то есть стартовать новую JVM, которой нужно время, чтобы прогреться. А значит, сборка будет осуществляться медленно. Запуская ту же сборку в уже прогретой JVM, в которой работает IDE, можно добиться существенного увеличения скорости сборки. Кроме того, в IDE возможно запускать сборку и публикацию JavaScript при изменении исходников. Таким образом можно добиться цикла, традиционного для обычного JavaScript — сохранили и обновили страницу в браузере. Собственно, эта возможность и реализована в плагине для Eclipse.
Другим моментом, который я считаю необходимым, является автоматическая настройка проекта. Например, если в проект приходит новый участник, то очень не хочется заставлять его читать длинный мануал по настройке проекта в IDE, который, к тому же, содержит ошибки, поэтому придётся ещё и потратить своё время на помощь новичку. Или, если мне захотелось поработать над проектом на даче с ноутбука, то очень не хочется повторять те шаги, которые я уже когда-то проделал в офисе. Если же проект собирается Maven, то IDE откроет и настроит его так, как нужно. Я не хотел лишать этой возможности разработчиков, которые используют TeaVM, поэтому создал так же и конфигуратор для m2e. Это позволило добиться того, что разработчик должен всего лишь импортировать проект Maven или создать проект из архетипа, после чего проект можно запускать на готовом сервере и наслаждаться разработкой, как видно из туториала.
Наконец, очень важной является возможность отлаживать код. В GWT есть DevMode, который запускает код в JVM и соединяется с браузером, передавая ему все нативные вызовы JavaScript. Однако, в связи с прекращением поддержки NPAPI в некоторых браузерах, стало невозможно дальше поддерживать плагин для браузеров, который принимал соединения и исполнял команды DevMode. Надо сказать, что это не разработчики GWT такие нехорошие, что не хотят переписывать плагин, это разработчики браузеров такие плохие, что не придумают, как же подружить JavaScript с синхронным вводом-выводом.
В качестве альтернативы команда GWT предлагает использовать SuperDevMode, который
основан на source maps. Source maps плохи тем, что разработчик вынужден отлаживать код не в том же месте, в котором он его пишет. Более того, браузер не предоставляет всех крутых фишек, которые доступны в IDE. Наконец, source maps попросту не могут переводить такие вещи, как имена переменных, полей, методов, классов.
Как же я поступил? Если браузер понимает source maps, то пусть source maps понимает и IDE. Но только пусть это будут улучшенные source maps. В итоге, помимо стандартных source maps, TeaVM генерирует свою отладочную информацию, а так же содержит плагин, который позволяет этой отладочной информацией воспользоваться в Eclipse. Если хотите посмотреть, что получилось, можете сами попробовать.
Подытоживая, хочу заметить, что плагин для Eclipse является не просто инструментом, а ещё и своего рода proof of concept, показывая, что на основе имеющегося в TeaVM функционала вполне возможно создавать плагины для IDE. Реализовать плагины к другим IDE теперь уже дело техники.
Планы
На самом деле, проекту ещё развиваться и развиваться. Планов много, вот лишь некоторые из них:
- План на ближайшее время — реализовать альтернативный бэкенд для libGDX. GWT не вполне удовлетворяет авторов фреймворка, кроме того, TeaVM уже работает быстрее, и ещё есть простор для дальнейших оптимизаций.
- Крутая библиотека для клиентской разработки. Сейчас всё, что есть, — это тонкие обёртки вокруг некоторых браузерных API. Разумеется, для разработки приложений нужно что-то поудобнее. TeaVM сам по себе никогда не включит каких-либо библиотек для разработки клиентских приложений. Это — совсем другой проект. Возможно, удастся взять готовую библиотеку, которая работает на том же GWT, и адаптировать её для TeaVM. Впрочем, уже есть один такой фреймворк, DukeScript, но мне категорически не нравится ни его реализация ни то, как он
недокументирован; тем не менее, я включил его поддержку в TeaVM. - Автоматическая генерация асинхронного кода. Не нравится мне лапша из callback'ов. А в силу того, что блокирующие операции в JavaScript отсутствуют как класс, их можно эмулировать, специальным образом преобразуя код.
- Множество оптимизаций. В движках JavaScript есть очень хорошие оптимизаторы, но они не всегда знают о коде то, что доступно статическому оптимизатору. В силу того, что TeaVM внутри использует представление SSA, различные оптимизации реализуются достаточно просто и безболезненно.
- Реализовать ещё больше классов из JDK.
- Реализовать поддержку инструкции INVOKEDYNAMIC, в частности, для того, чтобы можно было запускать лямбды из Java 8.
- Подружить TeaVM с Kotlin и Scala. В самом байткоде, который они генерируют, нет ничего особенного и TeaVM отлично переводит его в JavaScript. Однако, рантаймы этих языков используют классы JDK, которые не реализованы в TeaVM. Решается это либо реализацией данных классов, либо переписывание соответствующей части рантайма.
Почему не ...
… GWT?
Я достаточно много пишу на GWT, и я очень недоволен этим фреймворком. Идея генерировать JavaScript из исходников является крайне неудачной.
- Необходимо плодить костыли на уровне системы сборки, чтобы правильно подключать исходники всех библиотек к системе сборки.
- Нет поддержки других языков. Для некоторых языков, есть собственный аналоги GWT, например, Scala.js и нативная поддержка в Kotlin. Однако, у них есть другой недостаток: невозможность взаимодействия с существующими Java-библиотеками.
- GWT никак не интегрировать с утилитами, преобразующими или генерирующими байткод.
Помимо этого, у меня есть ряд претензий к тому, как реализован GWT, а именно:
- Команда GWT фактически отказалась от поддержки DevMode — единственной возможности отлаживать код. Есть SuperDevMode, но он крайне неудобен.
- GWT работает довольно-таки медленно. А тот факт, что он ещё и генерирует перестановки, делает сборку невыносимо долгой.
- Идея держать сгенерированный JavaScript на отдельном сервере побуждает плодить костыли и натыкаться на всевозможные проблемы, связанные с тем, что ресурсы (картинки, CSS), RPC и код находятся на разных серверах во время отладки, но при этом на одном сервере — в боевых условиях.
При этом писать на JavaScript мне не нравится тем более из-за динамической природы языка. Поэтому из двух зол я выбираю GWT, но мне хочется чего-то получше.
… emscripten?
На первый взгляд TeaVM очень похож на emscripten. Так почему я просто не перевожу байткод Java в LLVM? Ведь LLVM уже содержит готовую реализацию SSA. Всё дело в том, что LLVM очень низкоуровневый. Это хорошо, когда программа компилируется в нативный код, но вот когда она декомплилируется в высокоуровневый язык, получается некоторый оверхед. Ведь с JavaScript уже есть такие понятия, как поля, методы, GC. А в LLVM это всё пришлось бы снова эмулировать на JavaScript. Другая причина заключается в том, что TeaVM полностью написан на Java, компилятор можно, например, заэмбеддить, что не было бы возможным в случае в LLVM и emscripten. Наконец TeaVM использует чуть более мощные алгоритмы декомпиляции, чем emscripen.
Автор: konsoletyper