К основному содержанию
T TON Adoption
← Словарь
NODE/03 · Term

Continuation

Первичная вычислительная сущность TVM: «продолжение» — указатель на отрезок кода + контекст исполнения. Заменяет привычные процедуры и функции — все control-flow примитивы TVM строятся на continuation'ах.

Синонимы: continuation tvm, продолжение tvm

Continuation — фундаментальная вычислительная сущность TVM, заменяющая привычные понятия «функция», «процедура», «return-address». Это структура, содержащая указатель на отрезок кода + контекст исполнения (стек, регистры).

Зачем

В TVM нет инструкции CALL в Z80/x86-смысле — есть только манипуляции с continuation’ами. IFJMPREF { ... }, CALLREF, RETALT и т.д. — все они работают на continuation-уровне. Это даёт:

  • Высшие функции в FunC/Tact/Tolk «бесплатно»: continuation == first-class value.
  • Try/catch через c2-альтернативный continuation.
  • Loop’ы через recursion или через WHILE { c1 } { c2 }-комбинаторы.

Особенность для приходящих с EVM

Для разработчиков, приходящих с EVM, это самый необычный концепт TVM. Рекомендуется сразу почитать TVM Whitepaper §4 («Control flow, continuations, exceptions»), прежде чем писать собственный нетривиальный контракт. Без понимания continuation’ов невозможно прочитать TASM моста или биржи.

Регистры c0-c5: где живут continuations

TVM держит continuation’ы в специальных control-регистрах:

РегистрНазначение
c0Return continuation — куда прыгает RET/RETALT после завершения
c1Alternative continuation — для RETALT, отвечает за исключения
c2Exception handler — THROW дёргает именно его
c3Dictionary с procedure-IDs — служит как «функциональная таблица» контракта
c4Persistent storage cell — то, что записывается обратно в state
c5Actions output — список исходящих сообщений

При входе в get-method или receive-функцию runtime подкладывает c0 так, чтобы RET корректно завершил выполнение и вернул tuple результатов.

Пример: try/catch через c2

В FunC обработка ошибок выглядит как два continuation’а — нормальный поток и handler:

try {
  throw_unless(error_code::not_owner, equal_slices(sender, owner));
  do_owner_only_thing();
} catch (_, exno) {
  send_text_message(sender, "Access denied");
}

Компилятор разворачивает это в TVM-инструкции: PUSHCONT { ... } PUSHCONT { ... } TRY — два continuation’а кладутся на стек, TRY ставит второй в c2 и выполняет первый.

Пример: get-method ABI на continuation’ах

c3 — это словарь, в котором ключ — method-ID (CRC16 от имени), значение — continuation тела метода. Когда tonapi вызывает runGetMethod, TVM делает:

PUSHINT <method_id>
PUSH c3
DICTPUSHCONT  ; ищет в словаре, кладёт continuation
EXECUTE       ; вызывает

Если хочется добавить custom method в собственный контракт — нужно добавить запись в c3-словарь на этапе компиляции (global_method_id(...) в FunC, get fun в Tact).

Сравнение с EVM/Move

КонцептEVMMoveTVM
Вызов функцииCALL opcode, stack frameCall bytecode, framePUSHCONT + CALLREF — continuation as value
ВозвратRETURNRetRET → c0
ИсключенияREVERTabortTHROW → c2
Higher-orderНе поддерживается до Cancun (function pointer hack)Generics + closuresBuilt-in: continuation == first-class

EVM-разработчик привык к «методу с адресом в bytecode» — в TVM нужный кусок кода передаётся как параметр.

Где смотреть continuation’ы при дебаге

  • TON Sandbox (@ton/sandbox) — при execute step-by-step видны регистры c0/c1/c2 после каждой инструкции.
  • tonscan execution trace — показывает финальное значение c4 (storage delta) и c5 (out_msgs).
  • func-js compiler —debug-info — помечает места, где PUSHCONT соответствует исходной функции.

Связанные темы

  • TVM — общая архитектура виртуальной машины.
  • Get-метод — практическое применение c3-словаря.
  • Opcode — список инструкций, манипулирующих continuation’ами.

См. также