Мы уже некоторое время ищем безопасный для памяти язык программирования, который заменит C++ в Ladybird. Сначала мы изучили Swift, но взаимодействие с C++ так и не появилось, а поддержка платформ за пределами экосистемы Apple была ограничена. Война – это другая история. Экосистема системного программирования гораздо более зрелая, и многие из наших участников уже знают этот язык. В дальнейшем мы переписываем части Ladybird на Rust.
Почему ржавеет?
Когда мы первоначально оценивали Rust в 2024 году, мы отвергли его, поскольку он не подходил для ООП в стиле C++. Объектная модель веб-платформы во многом наследует ООП 1990-х годов, включая сборку мусора, глубокую иерархию наследования и т. д. Модель владения Rust не подходит для этого естественным образом.
Но после еще одного года борьбы пришло время сделать практический выбор. У Rust есть экосистема и гарантии безопасности, которые нам нужны. И Firefox, и Chromium уже начали включать Rust в свои кодовые базы, и мы считаем, что это идеальный выбор и для Ladybird.
портирование libjs
нашей первой целью было библиотеки libjs JavaScript-движок Ladybird. Лексер, синтаксический анализатор, AST и генератор байт-кода относительно автономны и имеют обширное тестовое покрытие через test262, что делает их естественной отправной точкой.
Для перевода я использовал Cloud Codes и Codex. Это была генерация кода под руководством человека, а не автономной. Я решил, что портировать, в каком порядке и как должен выглядеть код Rust. Сотни маленьких подсказок приводили агентов туда, куда нужно было идти. После первоначального перевода я провел несколько негативных обзоров, попросив разные модели проанализировать код на наличие ошибок и неправильных шаблонов.
Результат
С самого начала требовалось, чтобы выходные данные из обоих конвейеров были побайтно идентичными. В результате получилось около 25 000 линий ржавчины, а на завершение всего порта ушло около двух недель. Мне бы потребовались месяцы, чтобы сделать ту же работу вручную. Мы проверили, что каждый AST, созданный парсером Rust, идентичен C++, а весь байт-код, сгенерированный компилятором Rust, идентичен выходным данным компилятора C++. Нулевая регрессия по всем направлениям:
| набор тестов | тесты | Регрессия |
|---|---|---|
| тест262 | 52 898 | 0 |
| регрессионный тест божьей коровки | 12 461 | 0 |
Ни в одном из отслеживаемых нами тестов JS не наблюдается снижения производительности.
Помимо наборов тестов, я провел обширное тестирование, просматривая веб-страницы в пошаговом режиме, когда конвейеры C++ и Rust работают одновременно, проверяя, что выходные данные одинаковы для каждого фрагмента JavaScript, проходящего через них.
Если вы посмотрите на код, вы заметите, что он имеет сильную атмосферу «переведенного с C++». Это потому, что это Является Переведено с С++. Главным приоритетом для этого первого прохода является совместимость с нашим конвейером C++. Код Rust намеренно имитирует такие вещи, как шаблоны распределения регистров C++, так что оба компилятора создают идентичный байт-код. Чистота на втором месте. Мы знаем, что результат — это не идиоматическая ржавчина, и многое можно упростить, как только мы освоимся с отказом от конвейера C++. Это разъяснение придет со временем.
что будет дальше
Это не будет основной задачей проекта. Мы продолжим разработку движка на C++, а портирование подсистемы на Rust в долгосрочной перспективе станет второстепенным занятием. Новый код Rust будет сосуществовать с существующим C++ через четко определенные границы взаимодействия.
Мы хотим тщательно обдумать, какие части портировать и в каком порядке, поэтому усилиями по портированию управляет основная команда. Пожалуйста, согласуйте с нами, прежде чем начинать какие-либо работы по портированию, чтобы никто не тратил свое время на то, что мы не можем объединить.
Я знаю, что это будет спорный шаг, но я считаю, что это правильное решение для будущего Ladybird. :^)
Андреас Клинг
Основатель и председатель