Повторение18 / 19

Общая схема работы движка

Как V8 подготавливает код, выполняет функции и управляет памятью.

Подведём итог раздела «Движок JavaScript»: повторим, как движок подготавливает и выполняет код, хранит объекты и освобождает память.

V8 получает текст JavaScript и подготавливает инструкции для его выполнения. Рассмотрим функцию, которая создаёт объект врага:

function spawnEnemy() {
  return { hp: 100 };
}

От текста к выполнению

Интерактивная схема для примера выше. Каждый этап передаёт результат следующему. В V8 парсер получает токены от сканера по мере разбора; здесь эти действия разделены. Дерево и байткод показаны упрощённо.

  1. Сканер выделяет токены

    Разбивает текст на части: слово return, имя свойства hp, число 100, скобки и другие знаки.

    Текст → токены
    Токены → парсеру
    functionspawnEnemy(){return{hp:100};}
  2. Парсер строит AST

    По токенам собирает дерево, описывающее структуру программы. Здесь дерево показывает: функция возвращает объект со свойством hp: 100.

    Токены → дерево программы
    AST → генератору байткода
    • Функция spawnEnemy
      • Тело функции
        • Возврат return
          • Объект
            • Свойство hp
              • Число 100
  3. Генератор байткода создаёт инструкции

    Обходит AST и записывает операции для интерпретатора. В нашем примере их смысл: создать объект, затем вернуть его.

    AST → байткод
    Байткод → Ignition
    CREATE_OBJECT { hp: 100 }Создать объектRETURNВернуть его

    Учебный псевдобайткод: названия инструкций условные. Объект появится при вызове функции.

  4. Ignition выполняет байткод

    Читает инструкции и выполняет соответствующие операции. CPU при этом исполняет машинный код самого интерпретатора.

    Байткод → выполнение операций
    Результат вызова spawnEnemy() { hp: 100 }

    Ignition выполнил инструкции: объект создан и возвращён из функции.

V8 также может с помощью JIT-компилятора создать машинный код функции. Тогда CPU выполняет эту версию функции напрямую. В обоих случаях программа должна получить тот же результат.

Что происходит с вызовом и объектом

Когда программа выполняет const enemy = spawnEnemy(), происходит следующее:

  1. Начинается вызов. Движок сохраняет данные вызова в стеке: какую функцию выполняем и куда вернуться после неё.
  2. Создаётся объект. V8 выделяет для него память в куче и записывает hp: 100.
  3. Функция возвращает результат. Вызов завершается, а переменная enemy получает ссылку на созданный объект.
Стек вызововspawnEnemy() завершена

Программа продолжает работу после вызова.

Куча · память объектовenemy → { hp: 100 }

Объект доступен через ссылку из переменной.

Если объект сохранён ещё и в массиве врагов, доступ к нему остаётся через массив. Когда путей от корней к объекту больше нет, сборщик мусора сможет освободить его память.

Основной вывод

Сканер выделяет токены, парсер строит AST, генератор создаёт байткод, Ignition его выполняет. V8 также может создать машинный код функции. При выполнении движок ведёт стек вызовов и управляет памятью объектов.

Проверить по первоисточнику ↗

V8 · Scanner↗Сканер выделяет токены, а парсер использует их для построения AST.V8 · Генерация байткода из AST↗Генератор получает AST от парсера и создаёт последовательность инструкций для Ignition.V8 · Ignition↗Интерпретатор байткода в составе V8.V8 · Sparkplug↗Создание машинного кода из байткода и данные вызовов функций в стеке.V8 · Управление памятью↗Хранение объектов в куче и поиск доступных объектов по ссылкам.