Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 03.06 · 20 мин
Продвинутый
ContinuationsTVMControl FlowException HandlingLoops

Продолжения (Continuations)

Продолжения (continuations) — это мощная концепция TVM, позволяющая реализовать сложное управление потоком выполнения: от условных переходов до обработки исключений. Аналогия из реального мира — закладки в книге: вы можете сохранить текущее место (создать continuation), перейти к другой главе, а потом вернуться. Понимание continuations необходимо для чтения низкоуровневого кода и оптимизации газа в FunC/Tolk.

Continuation — уникальная концепция TVM, отсутствующая в EVM. Это “замороженное” вычисление: фрагмент кода вместе с контекстом, который можно сохранить, передать и выполнить позже.


Что такое Continuation?

Continuation в TVM — это значение на стеке, содержащее:

  • Код (code) — последовательность TVM-инструкций для выполнения
  • Стек (stack) — сохранённое состояние стека (опционально)
  • Registers — сохранённые управляющие регистры

По сути, continuation — это функция первого класса на уровне виртуальной машины.

Обычная инструкция:   ADD        → выполняется сразу
Continuation:         { ADD MUL } → "заморозка", выполнится позже

Типы Continuations

Ordinary Continuation (обычное продолжение)

Содержит код для последовательного выполнения. Создаётся при компиляции if/else, циклов.

// Tact код:
if (x > 0) {
    y = x * 2;
}

// TVM: создаёт 2 continuation
// cont_true: { PUSH s0; PUSHINT 2; MUL; POP y }
// cont_false: { NOP }
// IFELSE — выбирает и выполняет один из них

Exceptional Continuation (продолжение для ошибок)

Используется для обработки исключений (try/catch в Tact):

// Tact код:
try {
    let result = riskyOperation();
} catch (e) {
    // обработка ошибки
}

// TVM: устанавливает exceptional continuation
// c2 register ← { код обработки ошибки }
// Если THROW — TVM переключается на c2

Continuations для управления потоком

Условное выполнение

TVM не имеет JUMP и JUMPI как EVM. Вместо этого используются continuations:

// EVM (Ethereum):
PUSH label
JUMPI              ← прыгает к метке в коде

// TVM (TON):
PUSHCONT { ... }   ← создаёт continuation
PUSHCONT { ... }   ← создаёт continuation
IFELSE             ← выбирает и выполняет

Преимущество: нет произвольных прыжков в коде. Каждый “прыжок” — это выполнение continuation, что делает код более структурированным и безопасным.

NOTE

Ethereum EVM не имеет продолжений — управление потоком реализовано через JUMP/JUMPI (переходы по меткам в байткоде). Это более низкоуровневый подход, подверженный ошибкам (например, jump to invalid destination). TVM с continuations обеспечивает структурированное управление потоком.

Циклы

Циклы в TVM реализованы через continuations:

// REPEAT N — выполняет continuation N раз
PUSHINT 10
PUSHCONT {
    // тело цикла
    INC
}
REPEAT

// WHILE — выполняет пока условие истинно
PUSHCONT {
    // условие: оставляет TRUE/FALSE на стеке
    PUSH s0
    PUSHINT 100
    LESS
}
PUSHCONT {
    // тело цикла
    INC
}
WHILE

// UNTIL — выполняет до тех пор, пока не TRUE
PUSHCONT {
    // тело + условие выхода
    INC
    DUP
    PUSHINT 100
    EQUAL
}
UNTIL

Управляющие регистры

TVM использует специальные регистры для хранения continuations:

РегистрНазначение
c0Continuation для возврата (return address)
c1Альтернативное продолжение (для некоторых инструкций)
c2Exceptional continuation (обработка ошибок)
c3Текущий код контракта
c4Persistent data (storage контракта)
c5Очередь исходящих сообщений (actions)
c7Временные данные и контекст (время, адрес, баланс)

Когда функция вызывается, текущий continuation сохраняется в c0 (адрес возврата). При завершении TVM выполняет c0, возвращаясь к вызывающему коду.


Continuations и Exception Handling

Механизм исключений в TVM:

// TRY — устанавливает exceptional continuation
PUSHCONT {
    // обработчик ошибки (catch block)
    // на стеке: error_code, остаток стека
}
SETCONT c2          // c2 ← обработчик

// ... код, который может бросить исключение ...
PUSHINT 0
THROWIF 100        // если s0 != 0, бросить исключение 100

// При THROW:
// 1. TVM останавливает текущее выполнение
// 2. Помещает error_code на стек
// 3. Переключается на continuation из c2

Стандартные коды ошибок TVM:

КодОписание
0Успех
2Stack underflow
3Stack overflow
4Integer overflow
5Range check error
11Неизвестный opcode
13Out of gas

Практическое значение

Хотя разработчики на Tact и FunC не работают с continuations напрямую, понимание концепции помогает:

  1. Отлаживать ошибки — stacktrace TVM показывает continuations
  2. Оптимизировать gas — циклы с REPEAT дешевле, чем рекурсивные вызовы
  3. Понимать ограничения — максимальная глубина continuation stack влияет на сложность кода
  4. Читать FunC-код — FunC использует ifnot, repeat, while которые компилируются в continuation-инструкции

Частые ошибки

  1. Путают continuations с обычными функциями: continuation это замороженное состояние вычисления, включающее код и текущую позицию, а не просто вызов функции.
  2. Создают глубоко вложенные цепочки continuations, что приводит к избыточному расходу газа и сложности отладки.
  3. Не используют CATCH/TRY для обработки исключений, хотя они реализованы именно через continuations в TVM.
  4. Забывают, что каждый continuation занимает память (ячейку), и избыточное создание continuations увеличивает газовую стоимость.

Проверка знанийKnowledge check
Как TVM реализует условное выполнение (if/else) без инструкций JUMP/JUMPI, которые есть в EVM?
ОтветAnswer
TVM использует continuations — 'замороженные' вычисления, содержащие код и контекст. Для if/else компилятор создаёт два continuation (для true и false ветвей) и помещает их на стек. Инструкция IFELSE выбирает и выполняет одно из них в зависимости от условия. Это обеспечивает структурированное управление потоком без произвольных прыжков в байткоде.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7