Как стать автором
Обновить

Cтажировка в Haxe Foundation

Время на прочтение7 мин
Количество просмотров1.1K
Автор оригинала: Aurel Bily

Представляю вам перевод еще одного доклада с HaxeUp Sessions 2019 Linz, считаю что он хорошо дополняет предыдущий, т.к. продолжает тему изменений в Haxe, произошедших в 2019 году, а также немного рассказывает о его будущем.
Немного об авторе доклада: Аурел Били познакомился с Haxe, участвуя в различных гейм-джемах, и он продолжает в них участвовать (к примеру вот его игра с последнего Ludum Dare 45).


image


В настоящее время Аурел заканчивает обучение в Имперском колледже Лондона, что подразумевает обязательное прохождение стажировок. Первая стажировка, которую он проходил, была в удаленном офисе, дорога до которого занимала много времени. Поэтому он надеялся, что следующую практику удастся пройти удаленно.


image


Так получилось, что в Haxe Foundation долгое время не могли найти сотрудника на должность разработчика компилятора. Аурел решил испытать удачу и отправил письмо с заявкой об удаленной работе. Ему повезло — его приняли на шестимесячную стажировку с возможностью работать из Лондона.


image


При устройстве был оговорен круг задач, которыми будет заниматься Аурел (правда не все в итоге удалось реализовать).


image


Чем же он занимался?


Во-первых, документацией, которая находилась в печальном состоянии: были описаны все изменения в синтаксисе, новые возможности языка и компилятора, дополнены разделы, посвященные строкам, литералам и константам.


image


Вся документация была переведена из LaTeX в Markdown!


image


Во-вторых, форматирование кода стандартной библиотеки было приведено к единому стилю (т.к. над ней на протяжении более 10 лет работали разные люди с разным стилем оформления кода). Таким образом, в репозитории компилятора Haxe Аурел занял седьмое место по количеству добавленных строк кода :)


image


В третьих, Аурел поработал и над стандартной библиотекой и компилятором:
Например, у контейнера Map появился новый метод clear(), который удаляет все хранимые значения. Сделано это в первую очередь для удобства работы с контейнерами, созданными как final переменные (то есть им нельзя присвоить новое значение, но можно модифицировать их):


image


Для объектов типа Date появились методы для работы с датами в формате UTC (универсальное глобальное время). Работа над ними показала, насколько сложно реализовать единое API, одинаково работающее на всех 11 поддерживаемых в Haxe языках / платформах.


image


В старом компиляторе define’ы и мета-тэги были определены на OCaml, теперь же они описываются в формате JSON, что должно упростить их парсинг внешними утилитами (например, для автоматической генерации документации):


image


Также вы могли заметить, что на крупных проектах сервер компиляции начинает использовать много памяти.


image


Для решения этой проблемы Саймон Краевский и Аурел разработали бинарный формат hxb, который используется для сериализации типизированного AST. Теперь сервер компиляции может загрузить модуль в память, работать с ним до тех пор пока он нужен, а затем выгрузить его из памяти в файл в формате hxb и освободить занимаемую память.


image


Спецификация формата hxb доступна в отдельном репозитории, а текущая его реализация в компиляторе (с сериализатором / десериализатором) лежит в отдельной ветке Haxe. Работа над данной фичей пока что не закончена, и возможно она появится в Haxe 4.1.


image


Четвертым и основным направлением работы Аурела в ходе стажировки было создание нового асинхронного системного API — asys.


image


Потребность в его создании обусловлена тем, что существующее API не предоставляет легких способов выполнения системных операций асинхронно. Например, для работы с файлами асинхронно вам придется создать отдельный поток, в котором будут выполняться требуемые операции, и вручную управлять его состоянием. Кроме того, в текущем API не доступны все функциональные возможности для работы с UDP-сокетами, которые есть в стандартных библиотеках в других языках, нет поддержки IPC-сокетов.


image


При создании и реализации нового API возникает множество вопросов:


Как проектировать API? Может быть стоит взять в качестве примера какое-либо существующее? Ведь мы же не хотим создавать все с нуля, т.к. это потребует больше времени, а также может не прийтись по вкусу остальной команде и стать причиной долгих споров.


И, как уже упоминалось, актуальной для Haxe проблемой является реализация единого API для всех поддерживаемых платформ.


image


В качестве образца было выбрано API Node.js. Оно хорошо продумано, поддерживает необходимые системные функции и хорошо подходит для создания серверных приложений.


image


Но в то же время API Node.js — это Javascript API с отсутствием строгой типизации. Например, функции из модуля fs для работы с файловой системой могут принимать в качестве путей как строки, так и объекты типа Buffer и даже URL. А это уже не так хорошо подходит для Haxe.


image


Node.js в свою очередь использует библиотеку libuv, написанную на C. Работать с API libuv из Haxe напрямую было бы уже не так удобно: например, для того, чтобы асинхронно переименовать файл, вам потребовалось бы дополнительно создать объекты типа uv_loop_t (структура для управления циклом событий в libuv) и uv_fs_t (структура для описания запроса к файловой системе):


image


В итоге API Node.js и libuv были интегрированы следующим образом (на примере интерпретатора макросов eval и метода rename):


  • взяли АPI метода из Node.js, преобразовали его в Haxe, стараясь при этом стандартизировать типы аргументов и избавиться от избыточных для Haxe аргументов. Например, аргументы-пути (тип FilePath) — это абстракты над строками:

image


  • затем создали OCaml-биндинги для этого метода:

image


  • связали OCaml и C (с помощью CFFI — C Foreign Function Interface):

image


  • и, наконец, написали С-биндинги для вызова C-функций libuv из OCaml:

image


Аналогично было сделано для HashLink и Neko (пока что asys API реализован только для этих трех платформ). Как можно предположить, это потребовало много работы.


Аурел показал несколько небольших приложений, демонстрирующих работу asys API.
Первый пример — демонстрация асинхронного чтения содержимого файла. Пока что в коде явно вызываются методы для инициализации libuv (hl.Uv.init()) и запуска цикла работы приложения (hl.Uv.run()), связано это с тем, что работа над API еще не завершена (но в дальнейшем они будут добавляться автоматически):


image


Результат работы показанного кода:


image


Мы видим, что результаты работы вызванных методов AsyncFileSystem.readFile() выводятся в консоль после trace’а “after call”, который в коде вызывается после попыток прочитать содержимое файлов.


Второй пример — демонстрация асинхронной работы с DNS и IP-адресами.


image


В новом API определить имя хоста станет гораздо проще, а также появятся вспомогательные методы для работы с IP-адресами.


image


Третий пример — это простой TCP эхо-сервер, для создания которого достаточно всего трех строчек кода:


image


Четвертый пример — это демонстрация обмена информацией между процессами:
статический метод makeFrame() в рассматриваемом примере создает отдельные png-изображения:


image


а в методе main мы запускаем процесс ffmpeg, в который будем передавать сгенерированные в makeFrame() кадры:


image


и на выходе получим видео-файл:


image


И пятый пример — UDP video stream. Здесь как и в предыдущем примере запускается процесс ffmpeg, но в этот раз он воспроизводит видео и выводит его данные в стандартный поток вывода. Также создается UDP-сокет, который будет транслировать данные из процесса ffmpeg.


image


И, наконец, разбиваем данные, получаемые из ffmpeg, на меньшие “порции” и транслируем их на указанный порт:


image


И в результате получаем работающий видеопоток:


image


Обобщая вышесказанное, новое asys API включает в себя:


  • методы для работы с файловой системой, включая новые функции, которых не было в стандартной библиотеке (например, для изменения разрешений), а также асинхронные версии всех функций, доступных в старой стандартной библиотеке
  • поддержку асинхронной работы с TCP/UDP/IPC сокетами
  • методы для работы с DNS (пока что 2 метода: lookup и reverse)
  • а также методы для асинхронной работы с процессами.

image


Работа над asys API пока что не завершена, в настоящее время есть некоторые проблемы со сборщиком мусора при работе с библиотекой libuv. Pull Request с соответствующими изменениями еще не вмержен в основную ветку Haxe, в комментариях к нему приветствуются мнения по поводу имен новых методов, их сигнатур, а также документации.
Как уже упоминалось поддержка asys API реализована только для HashLink, Eval и Neko (в виде трех отдельных Pull Request’ов). У Аурела уже сформирован план, как добавить поддержку нового API для C++ и Lua. Реализация для остальных платформ потребует дополнительных исследований.


image


Возможно, что asys API станет доступно в Haxe 4.1 (но только для некоторых платформ).


Также Аурел рассказал о своем стороннем проекте — библиотеке ammer (который все же связан с его работой в Haxe Foundation).


image


Цель ammer — автоматизировать создание биндингов для библиотек на C так, чтобы их можно было использовать и в HashLink и HXCPP (в октябре 2018 года Ларс Дусе назначил вознаграждение за решение этой задачи).


Почему эта задача была актуальной? Дело в том, что хотя процесс создания биндингов для HashLink и HXCPP аналогичен, для каждой платформы придется писать свой связующий код (glue code).


Примерно то же самое Аурел делал, когда интегрировал библиотеку libuv в Haxe — для Eval, Neko и HashLink ему пришлось писать один и тот же код, который отличался только в деталях (вызов функций, отличия в работе FFI и др.):


image


Аналогичную работу требовалось проделать и на стороне Haxe для того, чтобы из него можно было вызывать нативные функции:


image


И идея ammer в том, чтобы взять Haxe-версию API, не загроможденную избыточной информацией, и сделать так, чтобы этот код каким-то образом заработал для всех платформ:


image


Что теперь требуется от пользователя ammer для работы с внешними библиотеками на C:


  • создаете Haxe-спецификацию для библиотеки, которая по существу является экстерном для используемой библиотеки
  • пишете код приложения
  • компилируете проект, указав пути к заголовочным файлам и файлам C-библиотеки
  • ...
  • profit

image


Под капотом ammer выполняет при этом следующую работу:


  • сопоставляет типы в зависимости от целевой платформы
  • автоматически генерирует C-код для вызова нативных функций
  • генерирует makefile, который используется для создания hdll, ndll файлов

image


В настоящий момент в ammer поддерживаются:


  • простые функции
  • define’ы из заголовочных файлов (в Haxe-коде к ним можно обращаться как к константам)
  • указатели

Планируется поддержка:


  • коллбеков (их пока что очень не хватает)
  • и структур (очень нужны для работы с C-API)

image


Сейчас ammer работает с C++, HashLink и Eval. И Аурел уверен, что сможет добавить поддержку остальных системных платформ.


image


Для демонстрации возможностей ammer Аурел показал небольшое приложение, в котором выполняется интерпретатор Lua:


image


Используемые в нем биндинги выглядят следующим образом:


image


Как видно некоторые методы закомментированы, т.к. в них используются коллбеки, поддержка которых еще на реализована, но Аурел надеется, что скоро сможет исправить это.


image


Итак, для чего может применяться ammer:


  • встраивание виртуальной машины Lua
  • создание приложений на SDL
  • возможна автоматизация работы с libuv (как было показано ранее, сейчас для работы с libuv требуется много кода, написанного вручную)
  • и, конечно же, значительно упростится использование множества других полезных библиотек на C (таких как OpenAL, Dear-imgui и др.)

image


И хотя стажировка Ауреля в Haxe Foundation закончилась, он планирует продолжать работу с Haxe, т.к. его обучение в колледже еще не завершено и ему предстоит еще написать выпускную работу. Аурел уже знает чему она будет посвящена — улучшение работы сборщика мусора в HashLink. Что же, это будет интересно!

Теги:
Хабы:
+7
Комментарии5

Публикации

Изменить настройки темы

Истории

Ближайшие события