Добро пожаловать в другие главы Давайте разберемся с Chrome V8

В этой статье мы поговорим о фрейме стека двух распространенных вызовов функций: «аргументы меньше объявленных параметров» и «аргументы больше объявленных параметров». Объясните, почему эти два вызова функций (несоответствие аргументов) не вызывают переполнения стека, но могут выполняться и правильно выводить результаты.

1. Введение

Фрейм стека используется для хранения аргументов, возврата вызываемого объекта, локальных переменных и регистров. Шаги создания кадра стека приведены ниже:

  • Параметры вызываемого абонента. Нажмите на стек, если есть параметры.
  • Нажмите на возврат вызываемого абонента.
  • Войдите в вызываемого абонента, нажмите ESP.
  • Сделайте EBP = ESP.
  • Зарезервируйте место в стеке для локальных переменных, если они есть.
  • Поместите регистр, который нужно защитить, в стек.

Вот пример:

На рис. 1 показан стек вызовов при выполнении add42.

Когда add42 выполняется, Ignition генерирует его байт-код, устанавливает стек, входит в пространство стека add42 и, наконец, возвращается к вызывающей стороне.

На рис. 2 показан кадр стека add42, созданный с помощью InterpreterPushArgsThenCall, который содержит дополнительные сведения. Давайте рассмотрим следующие два случая:

  • вызвать add42 без аргумента — add42()
  • вызовите add32 с тремя аргументами — add42(91,1,2)

В sloppy-режиме оба вызова выполняются нормально, и add(91,1,2) может вернуть ожидаемый результат — 133. Как работает кадр стека?

2. Регистрация, байткод

Прежде чем погрузиться во фрейм стека, давайте взглянем на регистры и байт-коды, которые помогут лучше понять стек. Вызов add42(91) приведен ниже.

V8 использует неотрицательные шестнадцатеричные целые числа для кодирования регистров, fa — регистры-r1, f9 — r2…

В последней строке — 5f fa f9 02 CallNoFeedback r1, r2-r3, fa — r1, f9 — r2, 02 — длина. Комбинация f9 и 02 означает: список регистров длины 2, начинающийся с r2.

Байт-код add42 приведен ниже:

В 1-й строке кода кодировка параметра a0 равна 02, что является смещением параметра в кадре стека, показанном на рисунке 2. Дополнение fa равно 6, а FP -6 точно соответствует регистру r1; FP +2 — это точно аргумент 91.

Перемещая указатель FP, вы можете быстро получить доступ к параметрам и регистрам, что упрощает разработку байт-кодов.

Формула адресации регистров: r[i]=FP-2-kFixedFrameHeaderSize-i. Формула адресации аргументов: a[i]=FP+2+parameter_count-i-1.

3. Рамка адаптера

Хотя от фрейма адаптера отказались, его изучение по-прежнему помогает лучше изучить фрейм стека.

add42()
add42(91,1,2,)

Мы знаем, что add42() возвращает Nan, add42(91,1,2) возвращает 133. На рис. 3 показан кадр адаптера.

Рама адаптера состоит из двух важных элементов:

  • Количество аргументов, число записывает количество аргументов, оно исходит из JSFunction. Для add42(x) это 1;
  • Слот аргументов, слот используется для хранения аргументов, количество слотов равно (1).

Для add42() отсутствует один аргумент, поэтому заполните слот значением Undefined, см. левую часть рисунка 3.

Для add42(91,1,2), несмотря на три аргумента, Number of arguments равно 1, поэтому заполните слот первым аргументом — 91.

Независимо от того, сколько аргументов передается, add42 всегда может получить правильные аргументы, поэтому он выполняется правильно и возвращает ожидаемое значение.

Фрейм адаптера использует число параметров и слот для нормализации несоответствия аргументов, чтобы функцию можно было вызвать правильно.

Почему структура адаптера удалена? Конечно, для повышения производительности!
При вызове функции создаются два стековых фрейма. Одна — это инфраструктура адаптера, а другая — вызываемая инфраструктура, что требует слишком много времени.

4. Стек кадра

Новый кадр стека удаляет кадр адаптера, но по-прежнему использует ряд аргументов, как показано на рисунке 4.

Целью нового фрейма стека является выполнение следующих четырех требований:

(1) Получить параметры и регистры, используя указатель FP и смещение.

(2) Нормализовать несоответствие аргументов, add42() и add42(91,1,2) могут вызываться правильно.

(3) Стек может быть корректно откатан при возврате вызываемого объекта.

(4) Откажитесь от фрейма адаптера, уменьшите количество сборок стека.

Давайте объясним, как выполнить эти четыре требования.

Для (1): порядок отправки и кодировка не изменяются, поэтому они выполняются.

Для (2): количество аргументов здесь. Когда длина args ›= Number, сохраняются только args[0:Number-1]. Если это ‹ Number, используйте Undefined в качестве отсутствующего параметра.

Для (3): Из рисунка 4 мы выполнили это требование.

Для (4): Рамка адаптера уже снята.

Код для построения кадра стека приведен ниже.

В приведенном выше коде показан процесс построения кадра стека, он также включает в себя некоторые подготовительные работы, связанные с архитектурой, подробности см. в файле builtins-x64.cc.

Приведенный выше код является записью для выполнения вызываемого объекта, байт-коды Add42 будут выполнены.

Хорошо, на этом мы закончили. Увидимся в следующий раз, берегите себя!

Мой блог cncyclops.com. Пожалуйста, свяжитесь со мной, если у вас есть какие-либо проблемы.

WeChat: qq9123013 Электронная почта: [email protected]