Перейти к содержанию

Как устроен компилятор (для продвинутых)

Конвейер загрузки

ScriptLoader.load: парсинг всех исходников → генерация всех классов (таблица функций — одна на скрипт: функция видна только внутри файла, где объявлена, — общее между файлами идёт через события, команды и глобалы) → defineClass в дочернем ScriptClassLoader (ключ — бинарное имя, не id) → резолв MethodHandle: триггеры/команды типом void(Object[], ExecContext), функции — MethodType.fromMethodDescriptorString. Ошибка любого скрипта — LoadException со всеми причинами сразу.

FASTSCRIPT_DUMP_DIR=path — сложить сгенерированные .class на диск для javap -c.

Представления и бокс

Operators.Rep: NUMBER("D"), BOOL("Z"), TEXT, OBJECT, VOID. Локалы всегда хранят ссылки (Storage.JvmLocal, kind отдельно); конвертация в OBJECT — no-op; бокс примитивов — ровно в одном месте (boxToObject/хелперы). Бокс эмитится сразу после операнда, иначе ляжет не на тот стек (VerifyError: Bad type on operand stack — см. историю: emitDispatch/pushLikeGet/индексная запись теряли массив без dup; контекстным вызовам не хватало receiver'а slot 1; give ждал int вместо double).

Почему сравнения с числами особенные

Operators.ordered для NUMBER/NUMBER — DCMPG + прыжок в true-ветку: <→IFLT, >→IFGT, <=→IFLE, >=→IFGE (инверсия здесь когда-то переворачивала все сравнения — бенч не ловил, т.к. там операнды ANY идут через Values.less/greater...). Смешанные kinds — боксинг обоих и Values.*; resultType для + с неизвестным — ANY, не NUMBER.

Диспетч и хот-путь

ScriptEngine держит переиспользуемый ExecContext на скрипт и spreadInvoker для триггеров/команд: steady-state — вызов хендлера без рефлексии и аллокаций. callFunction (API/бенч) боксит аргументы и идёт через invokeWithArguments — не горячий путь, так задумано.

Functions.KNOWN — список имя/арность, а не Map: две арности одного имени (substring/2, substring/3) ломали Map.ofEntries дубликатом ключа.

Релокация ASM

tools/Package: оригинальные ASM-джарники подаются входами вместе с build/classes — упаковщик переименовывает классы ASM и переписывает ссылки (compiler/Code уже смотрит на новый пакет). Карта — явная (mappingsFor из ASM-джарников): SimpleRemapper умеет только точные замены internal-name (проверено tools/RelocProbe), префиксы — нет.

Сеть и сборка

Зависимости тянет tools/Fetch.java через java.net.http (Maven Central + Paper repo, снапшоты, BOM/dependencyManagement, транзитивные compile/runtime) — поэтому бетховская сеть ей не мешает. bungeecord-chat в libs обязателен: без него не компилируется легаси-overload CommandSender.sendMessage (обход через Adventure не работает — javac всё равно требует класс).