Самое важное на конференции GWT.create 2015

gwt-create-logoВ январе прошла интересная конференция GWT.create, на ней было сделано несколько важных анонсов и представлено видение разработчиков GWT относительно будущего фреймворка. Я постараюсь рассказать вам о самых важных новостях из мира GWT разработки.

Основные компании участники:

  • Google
  • Vaadin
  • Sencha
  • Arcbees
  • LinkedIn

По ощущениям, вся конференция затевалась, чтобы заявить миру — GWT жив и будет жить. Считаю, что у них совсем неплохо получилось.

Ну а теперь про сами доклады.

Самый главный доклад

Keynote от компании Google.

Первая часть доклада посвящена обзору вышедшей в 2014 году версии 2.7:

  • Инкрементальная компиляция для SuperDevMode
  • Мгновенная пересборка при отсутствии изменений
  • В 5-8 раз ускорены критичные по скорости операции в Map (put/get/contains/iterator)
  • Улучшены методы equals/startsWith/endsWith/trim класса String
  • Исключения теперь используют ленивое разворачивание стека
  • Ускорены операции cast/instanceOf
  • Добавлен новый тип стилей GSS — Google Style Sheets
  • Новая аннотация @JsInterop, которая позволяет использовать JavaScript объекты так, как если бы они могли быть приведены к некоторому Java интерфейсу. Такой подход теперь рекомендуется вместо использования JSNI.

Во второй части Ray Cromwel рассказывает о реализации приложения Inbox — клиента для Gmail. Основное достижение разработчиков — общий код (до 60%) для 4х платформ: телефоны (Android/iOS), планшеты, веб-приложения. При этом код для веб-приложения компилируется при помощи GWT, код для Android компилируется как обычное приложение для Dalvik, код для iOS компилируется при помощи нового транслятора J2ObjC, который транслирует Java код в Objective-C. Выглядит всё это круто и довольно безумно.

Следом идёт анонс GWT 2.8, нам обещают:

  • Полную! поддержку Java 8
  • GSS стили по умолчанию вместо CssResource
  • Ускорение реализаций коллекций
  • Улучшение отладки в SuperDevMode (просмотр коллекций, читабельные идентификаторы)

Ну и в заключительной части упоминается GWT 3.0:

  • Релиз будет несовместим с веткой 2.x
  • Будет полностью удалён устаревший DevMode
  • Добавиться поддержка DeltaJS и ServiceWorker
  • Новая версия API для работы с DOM — Elemental 2.0
  • Поддержка ES6

Тут отдельно стоит поговорить о проекте ServiceWorker. По сути — это отдельный WebWorker, который будет исполнять все сетевые вызовы (или вызовы сервисов). Он позволяет инкапсулировать все сетевые вызовы и обеспечить работу JS приложений в отсутствии сети при помощи синхронизации и перехвата сетевых вызовов. К сожалению, сейчас он поддерживает (и видимо так и будет) только HTTPS на стороне сервера. Подробности тут.

Elemental 2.0 — это очень хорошо. Нам обещают автоматически сгенерированный API на основе спецификаций HTML5. Вам больше не придётся писать JSNI методы, если не нашлось Java API, поскольку API будет пополняться и обновляться очень быстро.

DeltaJS активно используется в Inbox и нереально доставляет. Доставляет версии приложения на устройство, при этом они хранятся в datastore, а новые версии запрашиваются с сервера на старте. DeltaJS строит VCDIFF патч между версией клиента и последней версией и пересылает по сети только его. Клиент применяет патч (пока не понятно как это быстро), регистрирует новую версию в хранилище и запускает её.

Внезапно, был представлен фреймворк Singular. Заявлено, что это такой классный Angular для Java, без процедуры dirty-checking для объектов, со сборкой шаблонов на этапе компиляции, быстрый и пригодный для мобильных устройств. Про Singular на конференции был отдельный доклад.

Доклад про внутренности компилятора.

В докладе подробно описываются шаги компиляции и применяемые оптимизации. Все оптимизации выполняются в цикле до тех пор, пока проход цикла вносит изменения в AST дерево.

Выполняемые оптимизаций:

  • Pruner — Удаляет недостижимые методы, поля и классы
  • Finalizer — Помечает как final поля, методы и классы, которые не изменяются после объявления. Классы помечаются final, если у них нет наследников, методы — если нет переопределений в наследниках.
  • MakeCallStatic — заменяет мономорфные вызовы методов (точно известно, какая версия метода должна быть вызвана во время исполнения) на статический вызов, если
  • TypeTightener — вычисляет наиболее точный тип выражений, значений, параметров:
    Object o = new Foo() превращается в Foo o = new Foo()
  • MethodCallTightener — заменяет вызовы методов на более специфичные, например:
    List x = new ArrayList(); x.(List)add(...); будет заменён на ArrayList x = new ArrayList(); x.(ArrayList)add(...);
  • DeadCodeEllimination — удаляет блоки кода, если точно известно значение условия, вычисляет выражения, в которых задействованы только константы
    if (false) { /*Код будет удалён*/ }
    x = 2 + 3 будет заменено на x = 5
  • MethodInliner — встраивает тело метода на место его вызова, для коротких методов
  • EnumOrdinalizer — заменяет использование перечислений на их Ordinal значения, там где это возможно (там где нет вызовов  name()  и toString() )
  • SameParameterValueOptimizer — если все вызовы метода передают в параметр одно и тоже значение, то будет создана версия метода без этого параметра
    foo(a, 5);  будет заменён на foo(a); // в метод добавится int b = 5;

Второй этап работы компилятора — нормализация, она подготовливает AST дерево к трансляции в JavaScript.

Этапы нормализации:

  • Devirtualizer — заменяет вызовы к строкам, примитивам и массивам на статические методы библиотеки GWT. Прототипы JavaScript объектов не изменяются.
  • CatchBlockNormalizer — заменяет множественные выражения catch для одного try блока, поскольку JavaScript поддерживает только один блок catch для блока try.
    try{} catch(T e1) { /*block 1*/ } catch(S e2) { /*block 2*/ }  будет заменено на

    Такое превращение не совсем легально с точки зрения Java, но лучше решения не нашлось.
  • LongCastNormalizer — вставляет явное приведение к типу Long, там где это требуется.
  • LongEmulationNormalizer — вставляет вызовы методов для арифметики с типом Long, вся она эмулируется библиотекой GWT LongLib, поскольку JavaScript не имеет встроенной поддержки Long.
  • ImplementCastsAndTypeChecks — заменяет приведения типов и проверки instanceOf на соответствующие вызовы библиотеки GWT.
  • ArrayNormalizer — заменяет операции с массивами на соответствующие вызовы времени исполнения. Для соблюдения контрактов Java добавляются соответствующие проверки, выбрасывающие исключения.
  • EqualityNormalizer — заменяет сравнения через == либо на вызовы JavaScript, либо на вызов библиотеки GWT.
  • ReplaceGetClassOverrides — заменяет вызовы  getClass()  на обращение к магическому полю this.___clazz
  • ResolveRuntimeTypeReferences — заменяет ссылки на типы, используемые в instanceOf на представление, соответствующее времени исполнения.

Третий этап компиляции — генерация AST дерева JavaScript.

Этапы подготовки JavaScript AST:

  • FixNameClashesVisitor — выполняет переименование переменных, поскольку в JavaScript семантика блочной видимости отличается от Java.
  • CreateNamesAndScopeVisitor — вычисляет области видимости JavaScript и назначает имена Java методам, полям и классам.
  • RecordCrossClassCalls — вычисляет, какие статические методы могут предполагать, что инициализация класса уже завершена. Это нужно для верной расстановки статических блоков инициализации.
  • GenerateJavaSciptVisitor — генерация узов AST дерева.

В конечном итоге код класса будет выглядеть как-то так:

После трансляции будет выполнен ещё один этап оптимизации для получившегося JavaScipt кода:

  • JsSymbolResolver — определяет область видимость и имя для каждого идентификатора.
  • JsStaticEval — вычисляет выражения над константами.
  • JsNormalizer — исправляет некорректные с точки зрения JavaScript выражения. Например (a, b)++  будет заменено на (a, b++) .
  • JsInliner — встраивает короткие JavaScript методы в место их вызова.
  • JsUnusedFunctionRemover — удаляет неиспользуемые функции.
  • JsDuplicatedFunctionRemover — заменяет функции с одинаковым строковым представлением кода на одну.
  • DuplicateExecuteOnceRemover — удаляет лишние инициализации класса.
  • JsNamer — изменяет (обфусцирует) идентификаторы.
  • JsStackEmulator — встраивает в код дополнительную информацию для лучшего представления стека вызовов.

В докладе описано намного больше того, что я могу осветить в этой статье. Всем советую посмотреть сам доклад.

Как они там инкрементальную компиляцию таки сделали.

Инкрементальная компиляция это такой больной вопрос, решение которого мы ждали больше 4х лет. И вот тут говорят, что наконец-то родили.

Начнём с того, что GWT выполняет глобальные оптимизации и, казалось, инкрементальная компиляция совсем невозможна.

Некоторые детали по реализации инкрементальной компиляции:

  • Во время полной компиляции создаются дополнительные индексы и карты зависимостей.
  • Во время перекомпиляции в соответствии с индексами и зависимостями обновляется сгенерированный JavaScript код.
  • Почти все фазы компиляции, включая оптимизацию и нормализацию могут исполняться инкрементально, за исключением генерации кода, SourceMaps и построения индексов.
  • Инкрементальная компиляция работает только с SuperDevMode, компиляция проекта остаётся полной при запуске из ant/maven/gradle.

В докладе освещены некоторые планы и идеи:

  • Избавится от «взрыва» интернационализации: N языков x M браузеров.
  • Замена SuperDevMode на сервер непрерывной компиляции (по типу FSC или Zinc).
  • Более читабельный вариант JavaScript в режиме без обфускации.
  • Кэширование приложения и фоновые обновления кода.

В целом конечно серебряной пули не получилось, остаётся надеяться что инкрементальная компиляция в SuperDevMode будет пригодна к использованию.

Заключение

В целом конференция вышла очень насыщенной по темам и докладчикам, GWT живее всех живых и рвётся ввысь!

Yuriy Artamonov on GithubYuriy Artamonov on LinkedinYuriy Artamonov on Twitter
Yuriy Artamonov
Software Developer
До последнего времени Юрий принимал активное участие в разработке опенсорс-фреймворка CUBA Platform, специализируясь на архитектуре и фронтенд-технологиях. Преподавал в Самарском университете разработку приложений для мобильных устройств, основы UI/UX и менторил студентов.

В настоящее время работает в компании JetBrains в команде IntelliJ IDEA Ultimate. Когда выдаётся свободное время, пишет статьи и контрибьютит в проекты с открытым исходным кодом. Обожает реализовывать странные идеи с лозунгом: «А почему бы и нет?».