Как SpiderMonkey реализовал приватные поля JavaScript

Приватное поле JavaScript с именем вида #x не является обычным свойством с необычным названием. Его нельзя получить через Reflect.get или перечисление собственных свойств. Инженеры SpiderMonkey реализовали это так, чтобы поведение соответствовало языковой семантике, хотя внутреннее устройство движка отличается от буквального описания в спецификации.

Модель и устройство движка

Для понимания полезна мысленная модель скрытой WeakMap, где объект служит ключом к приватному состоянию. При таком представлении каждое приватное поле ассоциируется с хранилищем, доступ к которому возможен только при наличии соответствующего ключа — объекта, объявляющего поле. Однако это не обязывает движок хранить отдельную WeakMap для каждого приватного поля.

В описанной реализации SpiderMonkey использует скрытые свойства со специальными идентификаторами и опирается на существующие механизмы оптимизации доступа к свойствам. Такой подход даёт тот же наблюдаемый результат: приватные поля не видны через стандартные механизмы отражения и перечисления, и доступ к ним возможен только в контексте, где поле объявлено.

Необходимые проверки и байткод

При обращении к приватному полю требуется выполнять строгие проверки. Если поле запрошено на объекте, где оно не было объявлено, это должно приводить к TypeError, а не возвращать undefined. Аналогично, повторное объявление того же приватного поля на одном классе должно вызывать ошибку. Для выполнения этих проверок в движке введён специальный байткод CheckPrivateField, который обеспечивает корректную валидацию на этапе выполнения.

Особенности при работе с Proxy

Особый случай — взаимодействие с Proxy. Приватное состояние прокси и приватное состояние его целевого объекта различаются: приватность определяется местом объявления поля, а не тем, через какие наблюдатели или ловушки происходит доступ. Из этого следует, что приватные поля нельзя корректно смоделировать только через обычные ловушки прокси без дополнительных механизмов в рантайме. В реализации движка сохранён инженерный принцип: важна воспроизводимость наблюдаемого поведения языка, даже если внутренние структуры и алгоритмы отличаются от формальных шагов спецификации.

В результате SpiderMonkey обеспечивает требуемую семантику приватных полей — невидимость через отражение и корректные ошибки при неправильном доступе — за счёт собственных внутренних структур и новых проверок в байткоде, а не через буквальную имитацию WeakMap для каждого поля.