Как избежать конфликтов идентификаторов между обфусцированными файлами
Когда несколько VM-обфусцированных файлов попадают в один бандл, они могут объявить один и тот же идентификатор верхнего уровня и уронить страницу на этапе разбора. Здесь объясняется, почему так происходит и как это исправить.
Симптом
Сборка проходит без ошибок, но браузер выбрасывает исключение на этапе разбора, ещё до запуска приложения:
Чтобы узнать, сколько чанков объявляют имя из ошибки, посчитайте файлы, содержащие его объявление:
-l выводит каждый подходящий файл один раз, а -w сопоставляет только целые слова, поэтому vmdX не учитывается. var не учитывается, потому что повторное объявление var само по себе не является ошибкой. Результат больше 1
означает, что имя объявлено более чем в одном чанке. Замените vmd на имя из вашего сообщения об ошибке.
Конфликты идентификаторов имеют значение, только когда объявления находятся в одной области видимости. Повторение одного имени в разных модулях или замыканиях ещё не доказывает конфликт; доказательство - ошибка разбора выше.
Почему так происходит
VM-пресеты используют Identifier Names Generator со значением mangled-shuffled. Он перебирает небольшой алфавит в
перемешанном порядке, а при VM-обфускации каждая переименованная глобальная переменная получает префикс vm
(значение identifiersPrefix по умолчанию). Перемешанный порядок кешируется на время работы процесса, поэтому файлы,
обфусцированные в одном процессе или с одним и тем же фиксированным seed, берут имена из одной последовательности:
первая глобальная переменная в каждом файле получает одинаковое имя, вторая - другое одинаковое имя, и так далее.
Каждый файл обфусцируется независимо, и для каждого генератор начинает свою последовательность заново. Когда два файла
оказываются в одной области видимости, имена конфликтуют. Два объявления const vmd = … верхнего уровня попадают в
одну область видимости, и парсер отвергает второе.
Опции, которые вводят больше идентификаторов верхнего уровня, повышают шансы, что два файла дойдут до одного и того же
сгенерированного имени. vmWrapTopLevelInitializers - одна из них, и все VM-пресеты уже её включают; опции вроде
vmDynamicOpcodes или vmBytecodeEncoding добавляют ещё.
Исправления
Лучше сначала собрать бандл, а затем обфусцировать его один раз. Если вы всё же обфусцируете файлы по отдельности и скрипты разделяют глобальную область видимости, выберите один из вариантов:
Включите
randomIdentifiersPrefix(рекомендуется)Каждый запуск обфускации получает случайный префикс, который добавляется к каждому глобальному идентификатору (при VM-обфускации он заменяет префикс
vmпо умолчанию). Имена из разных файлов больше не делят одно пространство имён, поэтому конфликты исчезают, и вам не нужно вручную согласовывать префиксы.Задайте уникальный
identifiersPrefixдля каждого файлаВручную передавайте свой префикс при обфускации каждого файла (например,
identifiersPrefix: 'auth_'для одного,checkout_для другого). Это работает, но при большом числе файлов легко ошибиться - лучше использовать случайный вариант выше.Переключите
identifierNamesGeneratorнаhexadecimalШестнадцатеричные имена используют гораздо большее пространство ключей, поэтому вероятность того, что два файла выдадут одинаковый идентификатор, намного ниже, но уникальность не гарантируется. Компромисс: идентификаторы длиннее, чем при
mangled-shuffled, поэтому бандл становится немного больше.Используйте пакетную обработку нескольких файлов в интерфейсе или проверьте плагин сборщика
Панель управления добавляет отдельный префикс для каждого файла при пакетной обфускации нескольких файлов, которая доступна на платных тарифах. Если вы используете плагин сборщика, проверьте его поведение, а не предполагайте, что он делает то же самое. Если вы подключаете обфускацию вручную через npm API, префикс нужно включить самостоятельно.
Чтобы проверить исправление, пересоберите и перезагрузите собранное приложение целиком и убедитесь, что SyntaxError
исчез. Имя из прежнего сообщения об ошибке больше не должно объявляться более чем в одном чанке; префикс меняет все
сгенерированные имена, поэтому команда grep выше говорит только об этом конкретном имени, а не о новых.
Связанные опции
randomIdentifiersPrefix- случайный префикс для каждой сборки (рекомендуется)identifiersPrefix- явная строка префиксаidentifierNamesGenerator- вид имён (mangled,hexadecimal, …)
