Spec-Zone.ru › Perl 5.32

perlguts

СОДЕРЖАНИЕ

  • ИМЯ
  • ОПИСАНИЕ
  • Переменные
    • Типы данных
    • Что такое "IV"?
    • Работа с SVs
    • Смещения
    • Что на самом деле хранится в SV?
    • Работа с AVs
    • Работа с HVs
    • Расширения API хешей
    • AVs, HVs и неопределённые значения
    • Ссылки
    • Благословенные ссылки и объекты класса
    • Создание новых переменных
    • Счётчики ссылок и смертность
    • Хранилища и глобы
    • SV с двойным типом
    • Только для чтения значения
    • Копирование при записи
    • Магические переменные
    • Назначение магии
    • Магические виртуальные таблицы
    • Поиск магии
    • Понимание магии связанных хешей и массивов
    • Локализация изменений
  • Подпрограммы
    • XSUB и стек аргументов
    • Автозагрузка с XSUBs
    • Вызов Perl-процедур из C-программ
    • Помещение C-значения в стек Perl
    • Скретчпады
    • Скретчпады и рекурсия
  • Выделение памяти
    • Выделение
    • Перевыделение
    • Перемещение
  • PerlIO
  • Компилируемый код
    • Дерево кода
    • Просмотр дерева
    • Компиляционный проход 1: проверка процедур
    • Компиляционный проход 1a: свёртка констант
    • Компиляционный проход 2: распространение контекста
    • Компиляционный проход 3: оптимизация "дырочного просмотра"
    • Подключаемые runops
    • Временные крючки области видимости
  • Просмотр внутренних структур данных с помощью функций dump
  • Как поддерживаются несколько интерпретаторов и конкурентность
    • Фон и PERL_IMPLICIT_CONTEXT
    • Что случилось с dTHR?
    • Как использовать всё это в расширениях?
    • Нужно ли делать что-то особенное, если я вызываю Perl из нескольких потоков?
    • Будущие планы и PERL_IMPLICIT_SYS
  • Внутренние функции
    • Форматированный вывод IV, UV и NV
    • Форматированный вывод SVs
    • Форматированный вывод строк
    • Форматированный вывод Size_t и SSize_t
    • Указатель на целое число и целое число на указатель
    • Обработка исключений
    • Документация исходного кода
    • Обратная совместимость
  • Поддержка Unicode
    • Что такое Unicode?
    • Как распознать строку UTF-8?
    • Как UTF-8 представляет символы Unicode?
    • Как Perl хранит строки UTF-8?
    • Как преобразовать строку в UTF-8?
    • Как сравнивать строки?
    • Есть ли что-то ещё, что мне нужно знать?
  • Пользовательские операторы
  • Стек
    • Стек значений
    • Стек меток
    • Стек временных переменных
    • Стек сохранения
    • Стек областей видимости
  • Динамическая область видимости и стек контекстов
    • Введение в стек контекстов
    • Добавление контекстов
    • Удаление контекстов
    • Повторное выполнение контекстов
  • Выделение операторов на основе фрагментов
  • АВТОРЫ
  • СМОТРИТЕ ТАКЖЕ

ИМЯ

perlguts - Введение в Perl API

ОПИСАНИЕ

Этот документ пытается описать использование Perl API, а также предоставить некоторые сведения об основных принципах работы ядра Perl. Он далёк от полноты и, вероятно, содержит много ошибок. Пожалуйста, направляйте любые вопросы или замечания автору ниже.

Переменные

Типы данных

Perl имеет три определения типов, которые обрабатывают три основных типа данных Perl:

SV  Scalar Value
AV  Array Value
HV  Hash Value

У каждого определения типа есть свои процедуры для работы с различными типами данных.

Что такое "IV"?

Perl использует специальное определение типа IV, которое является простым знаковым целочисленным типом, гарантированно достаточным для хранения указателя (а также целого числа). Кроме того, есть UV, который просто беззнаковый IV.

Perl также использует два специальных определения типов, I32 и I16, которые всегда будут по крайней мере 32-битными и 16-битными, соответственно. (Опять же, существуют U32 и U16.) Они обычно имеют точно 32 и 16 бит, но на Crays они оба будут 64-битными.

Работа с SVs

SV можно создать и загрузить одной командой. Существует пять типов значений, которые можно загрузить: целое значение (IV), беззнаковое целое значение (UV), число с плавающей запятой (NV), строка (PV) и другой скаляр (SV). ("PV" означает "Указатель на значение". Вы можете подумать, что он неправильно назван, потому что описан как указывающий только на строки. Однако возможно, что он указывает на другие вещи. Например, он может указывать на массив UV. Но использование его для нестрок требует осторожности, так как основное предположение многих внутренних механизмов заключается в том, что PV предназначены только для строк. Например, часто автоматически добавляется последовательность NUL. Использование для нестрок документировано только в этом абзаце.)

Семь процедур:

SV*  newSViv(IV);
SV*  newSVuv(UV);
SV*  newSVnv(double);
SV*  newSVpv(const char*, STRLEN);
SV*  newSVpvn(const char*, STRLEN);
SV*  newSVpvf(const char*, ...);
SV*  newSVsv(SV*);

STRLEN — целочисленный тип (Size_t, обычно определённый как size_t в config.h), гарантированно достаточный для представления размера любой строки, которую может обработать Perl.

В маловероятном случае, когда для инициализации SV требуется более сложная операция, вы можете создать пустой SV с newSV(len). Если len равно 0, возвращается пустой SV типа NULL, иначе возвращается SV типа PV со выделенными len + 1 (для NUL) байтами памяти, доступными через SvPVX. В обоих случаях SV имеет значение undef.

SV *sv = newSV(0);   /* no storage allocated  */
SV *sv = newSV(10);  /* 10 (+1) bytes of uninitialised storage
                      * allocated */

Для изменения значения уже существующего SV существует восемь процедур:

void  sv_setiv(SV*, IV);
void  sv_setuv(SV*, UV);
void  sv_setnv(SV*, double);
void  sv_setpv(SV*, const char*);
void  sv_setpvn(SV*, const char*, STRLEN)
void  sv_setpvf(SV*, const char*, ...);
void  sv_vsetpvfn(SV*, const char*, STRLEN, va_list *,
                                    SV **, Size_t, bool *);
void  sv_setsv(SV*, SV*);

Обратите внимание, что вы можете указать длину строки, которую нужно назначить, используя sv_setpvn, newSVpvn, или newSVpv, или позволить Perl рассчитать длину, используя sv_setpv или указав 0 в качестве второго аргумента для newSVpv. Однако будьте осторожны, так как Perl определит длину строки, используя strlen, которая зависит от того, заканчивается ли строка символом NUL, и не содержит ли она иначе символов NUL.

Аргументы sv_setpvf обрабатываются как sprintf, а отформатированный вывод становится значением.

sv_vsetpvfn является аналогом vsprintf, но позволяет указать указатель на список аргументов переменной или адрес и длину массива SVs. Последний аргумент указывает на булево значение; по возвращении, если это булево значение истинно, то для форматирования строки использовалась информация, зависящая от локали, и содержимое строки, следовательно, недостоверно (см. perlsec). Этот указатель может быть NULL, если эта информация не важна. Обратите внимание, что эта функция требует указания длины формата.

Функции sv_set*() недостаточно универсальны для работы со значениями, имеющими «магию». См. "Магические виртуальные таблицы" далее в этом документе.

Все SVs, содержащие строки, должны заканчиваться символом NUL. Если строка не заканчивается символом NUL, существует риск возникновения ошибок core dump и повреждений кода, который передает строку функциям C или системным вызовам, ожидающим строку, завершенную символом NUL. Собственные функции Perl обычно добавляют завершающий символ NUL по этой причине. Тем не менее, вы должны быть очень внимательны, передавая строку, хранящуюся в SV, функции C или системному вызову.

Для доступа к фактическому значению, на которое указывает SV, можно использовать макросы:

SvIV(SV*)
SvUV(SV*)
SvNV(SV*)
SvPV(SV*, STRLEN len)
SvPV_nolen(SV*)

которые автоматически преобразуют фактический скалярный тип в IV, UV, double или строку.

В макросе SvPV длина возвращаемой строки помещается в переменную len (это макрос, поэтому вы не используете &len). Если вам не важна длина данных, используйте макрос SvPV_nolen. Раньше в этом случае использовался макрос SvPV с глобальной переменной PL_na. Однако это может быть довольно неэффективно, потому что PL_na должен быть доступен в хранилище потоко-локальных данных в многопоточном Perl. В любом случае помните, что Perl допускает произвольные строки данных, которые могут содержать символы NUL и могут не заканчиваться символом NUL.

Также помните, что C не позволяет вам безопасно сказать foo(SvPV(s, len), len);. Это может работать с вашим компилятором, но не будет работать для всех. Разбейте подобные выражения на отдельные присваивания:

SV *s;
STRLEN len;
char *ptr;
ptr = SvPV(s, len);
foo(ptr, len);

Если вы хотите узнать, является ли скалярное значение ИСТИНОЙ, вы можете использовать:

SvTRUE(SV*)

Хотя Perl автоматически увеличивает строки, если вам нужно заставить Perl выделить больше памяти для вашего SV, вы можете использовать макрос

SvGROW(SV*, STRLEN newlen)

который определит, нужно ли выделять больше памяти. В таком случае он вызовет функцию sv_grow. Обратите внимание, что SvGROW может только увеличивать, а не уменьшать, выделенную память SV и не автоматически добавляет место для завершающего символа NUL (собственные функции строк Perl обычно выполняют SvGROW(sv, len + 1)).

Если вы хотите записать в буфер существующего SV и установить его значение в строку, используйте SvPV_force() или один из его вариантов, чтобы форсировать SV в PV. Это удалит различные типы не-строковости из SV, сохранив содержимое SV в PV. Это можно использовать, например, для добавления данных из функции API в буфер без дополнительных копирований:

(void)SvPVbyte_force(sv, len);
s = SvGROW(sv, len + needlen + 1);
/* something that modifies up to needlen bytes at s+len, but
   modifies newlen bytes
     eg. newlen = read(fd, s + len, needlen);
   ignoring errors for these examples
 */
s[len + newlen] = '\0';
SvCUR_set(sv, len + newlen);
SvUTF8_off(sv);
SvSETMAGIC(sv);

Если у вас уже есть данные в памяти или вы хотите упростить свой код, вы можете использовать один из вариантов sv_cat*(), например, sv_catpvn(). Если вы хотите вставить данные в произвольное место строки, вы можете использовать sv_insert() или sv_insert_flags().

Если вам не нужно существующее содержимое SV, вы можете избежать некоторых копирований с помощью:

SvPVCLEAR(sv);
s = SvGROW(sv, needlen + 1);
/* something that modifies up to needlen bytes at s, but modifies
   newlen bytes
     eg. newlen = read(fd, s. needlen);
 */
s[newlen] = '\0';
SvCUR_set(sv, newlen);
SvPOK_only(sv); /* also clears SVf_UTF8 */
SvSETMAGIC(sv);

Опять же, если у вас уже есть данные в памяти или вы хотите избежать сложности выше, вы можете использовать sv_setpvn().

Если у вас есть буфер, выделенный с помощью Newx(), и вы хотите установить его как значение SV, вы можете использовать sv_usepvn_flags(). Это имеет некоторые требования, если вы хотите избежать повторного выделения памяти Perl для подгонки завершающего NUL:

Newx(buf, somesize+1, char);
/* ... fill in buf ... */
buf[somesize] = '\0';
sv_usepvn_flags(sv, buf, somesize, SV_SMAGIC | SV_HAS_TRAILING_NUL);
/* buf now belongs to perl, don't release it */

Если у вас есть SV и вы хотите узнать, какой тип данных Perl считает хранящимся в нём, вы можете использовать следующие макросы для проверки типа SV.

SvIOK(SV*)
SvNOK(SV*)
SvPOK(SV*)

Вы можете получить и установить текущую длину строки, хранящейся в SV, с помощью следующих макросов:

SvCUR(SV*)
SvCUR_set(SV*, I32 val)

Вы также можете получить указатель на конец строки, хранящейся в SV, с помощью макроса:

SvEND(SV*)

Но обратите внимание, что эти последние три макроса верны только если SvPOK() истинно.

Если вы хотите добавить что-то в конец строки, хранящейся в SV*, вы можете использовать следующие функции:

void  sv_catpv(SV*, const char*);
void  sv_catpvn(SV*, const char*, STRLEN);
void  sv_catpvf(SV*, const char*, ...);
void  sv_vcatpvfn(SV*, const char*, STRLEN, va_list *, SV **,
                                                         I32, bool);
void  sv_catsv(SV*, SV*);

Первая функция рассчитывает длину строки, которая должна быть добавлена, используя strlen. Во второй вы сами указываете длину строки. Третья функция обрабатывает свои аргументы как sprintf и добавляет отформатированный вывод. Четвёртая функция работает как vsprintf. Вы можете указать адрес и длину массива SVs вместо аргумента va_list. Пятая функция расширяет строку, хранящуюся в первом SV, строкой, хранящейся во втором SV. Она также заставляет интерпретировать второй SV как строку.

Функции sv_cat*() не достаточно универсальны для работы со значениями, имеющими «магию». См. "Магические виртуальные таблицы" далее в этом документе.

Если вы знаете имя скалярной переменной, вы можете получить указатель на её SV, используя следующее:

SV*  get_sv("package::varname", 0);

Это возвращает NULL, если переменная не существует.

Если вы хотите узнать, является ли эта переменная (или любой другой SV) фактически defined, вы можете вызвать:

SvOK(SV*)

Скалярное значение undef хранится в экземпляре SV, называемом PL_sv_undef.

Его адрес можно использовать всякий раз, когда нужен SV*. Убедитесь, что вы не пытаетесь сравнить случайный sv с &PL_sv_undef. Например, при взаимодействии с Perl-кодом, это будет работать правильно для:

foo(undef);

Но не будет работать при вызове как:

$x = undef;
foo($x);

Итак, повторяем, всегда используйте SvOK() для проверки, определён ли sv.

Также вы должны быть осторожны при использовании &PL_sv_undef в качестве значения в AV или HV (см. "AV, HV и неопределённые значения").

Также существуют два значения PL_sv_yes и PL_sv_no, содержащие логические значения ИСТИНА и ЛОЖЬ соответственно. Как и PL_sv_undef, их адреса могут быть использованы всякий раз, когда нужен SV*.

Не обманывайте себя, думая, что (SV *) 0 то же самое, что и &PL_sv_undef. Рассмотрим этот код:

SV* sv = (SV*) 0;
if (I-am-to-return-a-real-value) {
        sv = sv_2mortal(newSViv(42));
}
sv_setsv(ST(0), sv);

Этот код пытается вернуть новый SV (содержащий значение 42), если он должен вернуть действительное значение, или undef в противном случае. Вместо этого он возвратил указатель NULL, который где-то в процессе приведёт к нарушению сегментации, ошибке шины или просто странным результатам. Измените ноль на &PL_sv_undef в первой строке, и всё будет в порядке.

Для освобождения созданного SV вызовите SvREFCNT_dec(SV*). Обычно этот вызов не нужен (см. "Счётчики ссылок и смертность").

Смещения

Perl предоставляет функцию sv_chop для эффективного удаления символов из начала строки; вы предоставляете ей SV и указатель на место внутри PV, и она отбрасывает всё, что находится перед указателем. Эффективность достигается посредством небольшого трюка: вместо фактического удаления символов, sv_chop устанавливает флаг OOK (смещение OK), чтобы сигнализировать другим функциям о том, что трюк со смещением используется, и перемещает указатель PV (называемый SvPVX). вперёд на количество удалённых байтов и соответствующим образом корректирует SvCUR и SvLEN. (Часть пространства между старым и новым указателями PV используется для хранения количества удалённых байтов.)

Следовательно, в этот момент начало буфера, который мы выделили, находится в позиции SvPVX(sv) - SvIV(sv) в памяти, а указатель PV указывает на середину этого выделенного хранилища.

Это лучше всего продемонстрировать на примере. Обычно copy-on-write предотвращает использование этого трюка оператором подстановки, но если вы можете создать строку, для которой copy-on-write недоступен, вы можете наблюдать его в действии. В текущей реализации последний байт буфера строки используется как счётчик ссылок copy-on-write. Если буфер недостаточно большой, copy-on-write пропускается. Сначала посмотрите на пустую строку:

% ./perl -Ilib -MDevel::Peek -le '$a=""; $a .= ""; Dump $a'
SV = PV(0x7ffb7c008a70) at 0x7ffb7c030390
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x7ffb7bc05b50 ""\0
  CUR = 0
  LEN = 10

Обратите внимание, что LEN равно 10. (Это может отличаться в зависимости от вашей платформы.) Увеличьте длину строки до значения, на единицу меньшего, чем 10, и выполните подстановку:

% ./perl -Ilib -MDevel::Peek -le '$a=""; $a.="123456789"; $a=~s/.//; \
                                                           Dump($a)'
SV = PV(0x7ffa04008a70) at 0x7ffa04030390
  REFCNT = 1
  FLAGS = (POK,OOK,pPOK)
  OFFSET = 1
  PV = 0x7ffa03c05b61 ( "\1" . ) "23456789"\0
  CUR = 8
  LEN = 9

Здесь количество удалённых байтов (1) показано далее как OFFSET. Часть строки между "реальным" и "имитационным" началом показана в скобках, а значения SvCUR и SvLEN отражают имитационное начало, а не реальное. (Первый символ буфера строки, по-видимому, изменился здесь на "\1", а не "1", потому что текущая реализация хранит счётчик смещения в буфере строки. Это может измениться.)

Нечто подобное трюку со смещением выполняется над AV для обеспечения эффективного сдвига и удаления начала массива; в то время как AvARRAY указывает на первый элемент массива, видимый из Perl, AvALLOC указывает на реальное начало массива C. Они обычно совпадают, но операция shift может быть выполнена путём увеличения AvARRAY на единицу и уменьшения AvFILL и AvMAX. Опять же, расположение реального начала массива C имеет значение только при освобождении массива. См. av_shift в файле av.c.

Что на самом деле хранится в SV?

Напомним, что обычный метод определения типа скаляра — это использование макросов Sv*OK. Поскольку скаляр может быть как числом, так и строкой, эти макросы обычно всегда возвращают ИСТИНУ, и вызов макросов Sv*V выполнит соответствующее преобразование строки в целое число/дробное число или целого/дробного числа в строку.

Если вам действительно нужно узнать, имеете ли вы указатель на целое число, дробное число или строку в SV, вы можете использовать следующие три макроса вместо этого:

SvIOKp(SV*)
SvNOKp(SV*)
SvPOKp(SV*)

Они покажут, имеете ли вы действительно указатель на целое число, дробное число или строку, хранящийся в вашем SV. «p» означает «приватный».

Существуют различные способы отличия флагов private и public. Например, в perl 5.16 и более ранних версиях связанный SV может иметь действительное значение в слоте IV (так что SvIOKp истинно), но данные следует получать через процедуру FETCH, а не напрямую, поэтому SvIOK ложно. (В perl 5.18 и более поздних версиях связанные скаляры используют флаги так же, как и несвязанные скаляры.) Другой случай — когда произошла числовая конвертация, а точность была потеряна: только флаг private установлен для значений с «потерей». Таким образом, когда NV преобразуется в IV с потерей, SvIOKp, SvNOKp и SvNOK будут установлены, в то время как SvIOK не будет.

В общем случае лучше использовать макросы Sv*V.

Работа с AVs

Существует два способа создания и загрузки AV. Первый метод создает пустой AV:

AV*  newAV();

Второй метод создает AV и сразу заполняет его SVs:

AV*  av_make(SSize_t num, SV **ptr);

Второй аргумент указывает на массив, содержащий num SV*. После создания AV SVs можно уничтожить, если это необходимо.

После создания AV можно выполнить следующие операции:

void  av_push(AV*, SV*);
SV*   av_pop(AV*);
SV*   av_shift(AV*);
void  av_unshift(AV*, SSize_t num);

Это должны быть знакомые операции, за исключением av_unshift. Эта процедура добавляет num элементы в начало массива со значением undef. Затем необходимо использовать av_store (описано ниже), чтобы присвоить значения этим новым элементам.

Вот некоторые другие функции:

SSize_t av_top_index(AV*);
SV**    av_fetch(AV*, SSize_t key, I32 lval);
SV**    av_store(AV*, SSize_t key, SV* val);

Функция av_top_index возвращает наибольшее индексное значение в массиве (так же, как $#array в Perl). Если массив пустой, возвращается -1. Функция av_fetch возвращает значение по индексу key, но если lval не равно нулю, то av_fetch сохранит значение undef в этом индексе. Функция av_store сохраняет значение val по индексу key, и не увеличивает счетчик ссылок val. Таким образом, вызывающая сторона отвечает за это, и если av_store возвращает NULL, вызывающая сторона должна уменьшить счетчик ссылок, чтобы избежать утечки памяти. Обратите внимание, что av_fetch и av_store оба возвращают SV** , а не SV* как значение.

Ещё несколько:

void  av_clear(AV*);
void  av_undef(AV*);
void  av_extend(AV*, SSize_t key);

Функция av_clear удаляет все элементы в массиве AV*, но не удаляет сам массив. Функция av_undef удаляет все элементы в массиве и сам массив. Функция av_extend расширяет массив, чтобы он содержал как минимум key+1 элементов. Если key+1 меньше текущей длины выделенного массива, ничего не происходит.

Если вы знаете имя переменной массива, вы можете получить указатель на его AV, используя следующее:

AV*  get_av("package::varname", 0);

Это возвращает NULL, если переменная не существует.

См. "Understanding the Magic of Tied Hashes and Arrays" для получения дополнительной информации о том, как использовать функции доступа к массивам для связанных массивов.

Работа с HVs

Для создания HV используется следующая процедура:

HV*  newHV();

После создания HV возможны следующие операции:

SV**  hv_store(HV*, const char* key, U32 klen, SV* val, U32 hash);
SV**  hv_fetch(HV*, const char* key, U32 klen, I32 lval);

Параметр klen — длина ключа, передаваемого в функцию (обратите внимание, что вы не можете передать 0 как значение для klen, чтобы сказать Perl измерить длину ключа). Аргумент val содержит указатель SV на скаляр, хранящийся в массиве, а hash — предварительно вычисленное значение хеша (ноль, если вы хотите, чтобы hv_store вычислял его для вас). Параметр lval указывает, является ли этот вызов частью операции записи, в этом случае новое неопределённое значение будет добавлено в HV с предоставленным ключом, и hv_fetch вернёт так, как будто значение уже существовало.

Помните, что hv_store и hv_fetch возвращают SV** , а не просто SV*. Чтобы получить значение скаляра, сначала необходимо разыменовать возвращаемое значение. Однако следует проверить, чтобы возвращаемое значение не было NULL, прежде чем его разыменовывать.

Первая из этих двух функций проверяет, существует ли запись в таблице хешей, а вторая удаляет её.

bool  hv_exists(HV*, const char* key, U32 klen);
SV*   hv_delete(HV*, const char* key, U32 klen, I32 flags);

Если flags не включает флаг G_DISCARD, то hv_delete создаст и вернёт смертную копию удалённого значения.

И ещё несколько дополнительных функций:

void   hv_clear(HV*);
void   hv_undef(HV*);

Как и их аналоги AV, hv_clear удаляет все записи в таблице хешей, но не удаляет саму таблицу хешей. hv_undef удаляет и записи, и саму таблицу хешей.

Perl хранит фактические данные в связанном списке структур с типом данных HE. Они содержат фактические указатели на ключ и значение (плюс дополнительные административные данные). Ключ — указатель на строку; значение — SV*. Однако, если у вас есть HE*, чтобы получить фактический ключ и значение, используйте указанные ниже процедуры.

I32    hv_iterinit(HV*);
        /* Prepares starting point to traverse hash table */
HE*    hv_iternext(HV*);
        /* Get the next entry, and return a pointer to a
           structure that has both the key and value */
char*  hv_iterkey(HE* entry, I32* retlen);
        /* Get the key from an HE structure and also return
           the length of the key string */
SV*    hv_iterval(HV*, HE* entry);
        /* Return an SV pointer to the value of the HE
           structure */
SV*    hv_iternextsv(HV*, char** key, I32* retlen);
        /* This convenience routine combines hv_iternext,
           hv_iterkey, and hv_iterval.  The key and retlen
           arguments are return values for the key and its
           length.  The value is returned in the SV* argument */

Если вы знаете имя переменной хеша, вы можете получить указатель на её HV, используя следующее:

HV*  get_hv("package::varname", 0);

Это возвращает NULL, если переменная не существует.

Алгоритм хеширования определён в макросе PERL_HASH.

PERL_HASH(hash, key, klen)

Точное исполнение этого макроса варьируется в зависимости от архитектуры и версии perl, а возвращаемое значение может меняться при каждом вызове, поэтому значение действительно только в течение одного процесса perl.

См. "Understanding the Magic of Tied Hashes and Arrays" для получения дополнительной информации о том, как использовать функции доступа к хешам для связанных хешей.

Расширения API хешей

Начиная с версии 5.004, также поддерживаются следующие функции:

HE*     hv_fetch_ent  (HV* tb, SV* key, I32 lval, U32 hash);
HE*     hv_store_ent  (HV* tb, SV* key, SV* val, U32 hash);

bool    hv_exists_ent (HV* tb, SV* key, U32 hash);
SV*     hv_delete_ent (HV* tb, SV* key, I32 flags, U32 hash);

SV*     hv_iterkeysv  (HE* entry);

Обратите внимание, что эти функции принимают SV* ключи, что упрощает написание кода расширения, работающего со структурами хешей. Эти функции также позволяют передавать SV* ключи функциям tie без необходимости преобразования ключей в строки (в отличие от предыдущего набора функций).

Они также возвращают и принимают целые записи хеша (HE*), что делает их использование более эффективным (поскольку номер хеша для определённой строки не нужно вычислять каждый раз). См. perlapi для подробных описаний.

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

HePV(HE* he, STRLEN len)
HeVAL(HE* he)
HeHASH(HE* he)
HeSVKEY(HE* he)
HeSVKEY_force(HE* he)
HeSVKEY_set(HE* he, SV* sv)

Эти два макроса нижнего уровня определены, но должны использоваться только при работе с ключами, которые не являются SV*:

HeKEY(HE* he)
HeKLEN(HE* he)

Обратите внимание, что как hv_store, так и hv_store_ent не увеличивают счетчик ссылок хранящегося val, что является обязанностью вызывающей стороны. Если эти функции возвращают значение NULL, вызывающая сторона обычно должна уменьшить счетчик ссылок val, чтобы избежать утечки памяти.

AVs, HVs и неопределённые значения

Иногда необходимо хранить неопределённые значения в AV или HV. Хотя это может быть редким случаем, это может быть непросто. Это потому, что вы привыкли использовать &PL_sv_undef , если вам нужно неопределённое SV.

Например, интуиция подсказывает, что этот код XS:

AV *av = newAV();
av_store( av, 0, &PL_sv_undef );

эквивалентен этому коду Perl:

my @av;
$av[0] = undef;

К сожалению, это не так. В perl 5.18 и более ранних версиях AV используют &PL_sv_undef в качестве маркера, указывающего, что элемент массива ещё не инициализирован. Таким образом, exists $av[0] было бы истинным для вышеприведённого кода Perl, но ложным для массива, сгенерированного кодом XS. В perl 5.20 хранение &PL_sv_undef создаёт только для чтения элемент, потому что хранится сам скаляр &PL_sv_undef, а не его копия.

Аналогичные проблемы могут возникнуть при хранении &PL_sv_undef в HVs:

hv_store( hv, "key", 3, &PL_sv_undef, 0 );

Это действительно сделает значение undef, но если вы попытаетесь изменить значение key, вы получите следующую ошибку:

Modification of non-creatable hash value attempted

В perl 5.8.0 &PL_sv_undef также использовался для маркировки заглушек в ограниченных хешах. Это приводило к тому, что такие записи хеша не отображались при итерации по хешу или проверке ключей с помощью функции hv_exists.

Вы можете столкнуться с подобными проблемами, когда сохраняете &PL_sv_yes или &PL_sv_no в AV или HV. Попытка изменить такие элементы даст следующую ошибку:

Modification of a read-only value attempted

Короче говоря, можно использовать специальные переменные &PL_sv_undef, &PL_sv_yes и &PL_sv_no с AV и HV, но нужно обязательно понимать, что вы делаете.

В общем, если вы хотите сохранить неопределённое значение в AV или HV, не следует использовать &PL_sv_undef, а следует создать новое неопределённое значение с помощью функции newSV, например:

av_store( av, 42, newSV(0) );
hv_store( hv, "foo", 3, newSV(0), 0 );

Ссылки

Ссылки — это особый тип скаляров, которые указывают на другие типы данных (включая другие ссылки).

Для создания ссылки используйте любую из следующих функций:

SV* newRV_inc((SV*) thing);
SV* newRV_noinc((SV*) thing);

Аргумент thing может быть любым из SV*, AV* или HV*. Функции идентичны, за исключением того, что newRV_inc увеличивает счётчик ссылок thing, в то время как newRV_noinc этого не делает. По историческим причинам newRV является синонимом newRV_inc.

После получения ссылки можно использовать следующий макрос для разыменования ссылки:

SvRV(SV*)

затем вызывайте соответствующие процедуры, преобразуя возвращаемое значение SV* в AV* или HV*, если это необходимо.

Чтобы определить, является ли SV ссылкой, можно использовать следующий макрос:

SvROK(SV*)

Чтобы узнать, к какому типу значения указывает ссылка, используйте следующий макрос и затем проверьте возвращаемое значение.

SvTYPE(SvRV(SV*))

Наиболее полезные типы, которые будут возвращены, — это:

SVt_PVAV    Array
SVt_PVHV    Hash
SVt_PVCV    Code
SVt_PVGV    Glob (possibly a file handle)

Любое возвращаемое числовое значение, которое меньше SVt_PVAV, будет скаляром в какой-либо форме.

См. "svtype" в perlapi для получения дополнительной информации.

Благословенные ссылки и объекты классов

Ссылки также используются для поддержки объектно-ориентированного программирования. В лексиконе ООП perl объект — это просто ссылка, которая была благословлена в пакет (или класс). После благословения программист теперь может использовать ссылку для доступа к различным методам в классе.

Ссылку можно добавить в пакет с помощью следующей функции:

SV* sv_bless(SV* sv, HV* stash);

Аргумент sv должен быть значением ссылки. Аргумент stash определяет, к какому классу будет принадлежать ссылка. Смотрите "Stashes and Globs" для информации о преобразовании имён классов в стеки.

/* Все еще в разработке */

Следующая функция повышает rv до ссылки, если это не ссылка. Создаёт новый SV для указания на rv. Если classname не null, SV добавляется в указанный класс. Возвращается SV.

SV* newSVrv(SV* rv, const char* classname);

Следующие три функции копируют целое число, беззнаковое целое число или double в SV, ссылка на который rv. SV благословляется, если classname не null.

SV* sv_setref_iv(SV* rv, const char* classname, IV iv);
SV* sv_setref_uv(SV* rv, const char* classname, UV uv);
SV* sv_setref_nv(SV* rv, const char* classname, NV iv);

Следующая функция копирует значение указателя (адрес, а не строку!) в SV, ссылка на который rv. SV благословляется, если classname не null.

SV* sv_setref_pv(SV* rv, const char* classname, void* pv);

Следующая функция копирует строку в SV, ссылка на который rv. Установите длину в 0, чтобы позволить Perl рассчитать длину строки. SV благословляется, если classname не null.

SV* sv_setref_pvn(SV* rv, const char* classname, char* pv,
                                                     STRLEN length);

Следующая функция проверяет, благословен ли SV в указанный класс. Она не проверяет отношения наследования.

int  sv_isa(SV* sv, const char* name);

Следующая функция проверяет, является ли SV ссылкой на благословенный объект.

int  sv_isobject(SV* sv);

Следующая функция проверяет, является ли SV производным от указанного класса. SV может быть либо ссылкой на благословенный объект, либо строкой, содержащей имя класса. Это функция, реализующая функциональность UNIVERSAL::isa.

bool sv_derived_from(SV* sv, const char* name);

Чтобы проверить, есть ли у вас объект, производный от определённого класса, нужно написать:

if (sv_isobject(sv) && sv_derived_from(sv, class)) { ... }

Создание новых переменных

Чтобы создать новую переменную Perl со значением undef, доступной из вашего скрипта Perl, используйте следующие процедуры в зависимости от типа переменной.

SV*  get_sv("package::varname", GV_ADD);
AV*  get_av("package::varname", GV_ADD);
HV*  get_hv("package::varname", GV_ADD);

Обратите внимание на использование GV_ADD в качестве второго параметра. Теперь новую переменную можно установить, используя соответствующие процедуры для типа данных.

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

GV_ADDMULTI

Помечает переменную как определённую несколько раз, предотвращая:

Name <varname> used only once: possible typo

предупреждение.

GV_ADDWARN

Выводит предупреждение:

Had to create <varname> unexpectedly

если переменная не существовала до вызова функции.

Если вы не указываете имя пакета, переменная создаётся в текущем пакете.

Счётчики ссылок и смертность

Perl использует механизм сбора мусора, основанный на счётчиках ссылок. SV, AV или HV (xV в кратком виде далее) начинают свою жизнь со счётчиком ссылок 1. Если счётчик ссылок xV когда-либо упадет до 0, он будет уничтожен, а его память станет доступной для повторного использования. На самом базовом внутреннем уровне счётчики ссылок можно изменять с помощью следующих макросов:

int SvREFCNT(SV* sv);
SV* SvREFCNT_inc(SV* sv);
void SvREFCNT_dec(SV* sv);

(Также существуют версии макросов инкремента и декремента с суффиксом, для ситуаций, когда полная общность этих основных макросов может быть заменена на некоторую производительность.)

Однако, способ, которым программист должен мыслить о ссылках, заключается не столько в счётчике ссылок, сколько в владении ссылками. Ссылка на xV может принадлежать любому из различных сущностей: другому xV, интерпретатору Perl, структуре данных XS, фрагменту исполняемого кода или динамической области. В целом xV не знает, какие сущности владеют ссылками на него; он только знает, сколько ссылок существует, что и есть счётчик ссылок.

Для правильного поддержания счётчиков ссылок важно отслеживать ссылки, которые манипулирует код XS. Программист должен всегда знать, откуда пришла ссылка и кто ею владеет, и быть в курсе любого создания или уничтожения ссылок, а также любых передачь владения. Поскольку владение не представлено явно в структурах данных xV, единственное, что действительно нужно поддерживать кодом, — это счётчик ссылок, а это означает, что это понимание владения фактически не видно в коде. Например, передача владения ссылкой от одного владельца другому нисколько не изменяет счётчик ссылок, поэтому может быть достигнута без какого-либо кода. (Код передачи не трогает ссылаемый объект, но должен обеспечить, чтобы бывший владелец знал, что он больше не владеет ссылкой, а новый владелец знал, что теперь владеет.)

xV, видимый на уровне Perl, не должен стать неуказанным и, следовательно, быть уничтожен. Обычно объект станет неуказанным только тогда, когда он больше не виден, часто по тем же причинам, которые делают его невидимым. Например, значение ссылки Perl (RV) владеет ссылкой на свой референт, поэтому если RV перезаписывается, эта ссылка уничтожается, и в результате может быть уничтожен референт, который больше недоступен.

У многих функций есть некая манипуляция ссылками в рамках их назначения. Иногда это документируется с точки зрения владения ссылками, а иногда (менее информативно) — с точки зрения изменений в счётчиках ссылок. Например, функция newRV_inc() документирована как создающая новую RV (со счётчиком ссылок 1) и инкрементирующая счётчик ссылок референта, который был передан вызывающей стороной. Это лучше всего понять как создание новой ссылки на референт, которым владеет созданная RV, и возвращение вызывающей стороне владения единственной ссылкой на RV. Функция newRV_noinc() вместо этого не увеличивает счётчик ссылок референта, но RV тем не менее заканчивает владением ссылкой на референт. Таким образом, подразумевается, что вызывающая сторона newRV_noinc() отказывается от ссылки на референт, что делает эту концептуально более сложной операцией, даже если она меньше воздействует на структуры данных.

Например, представьте, что вы хотите вернуть ссылку из функции XSUB. Внутри процедуры XSUB вы создаёте SV, который первоначально имеет только одну ссылку, которой владеет процедура XSUB. Эта ссылка должна быть удалена до завершения процедуры, иначе она будет утечкой, что помешает SV когда-либо быть уничтоженным. Таким образом, для создания RV, ссылающейся на SV, наиболее удобно передать SV в newRV_noinc(), которая использует эту ссылку. Теперь процедура XSUB больше не владеет ссылкой на SV, но владеет ссылкой на RV, которая, в свою очередь, владеет ссылкой на SV. Затем владение ссылкой на RV передаётся путём возвращения RV из XSUB.

Есть несколько удобных функций, которые могут помочь с уничтожением xV. Эти функции вводят понятие «смертность». Много документации говорит об xV как о смертельном, но это вводит в заблуждение. На самом деле ссылка на xV смертельна, и может быть более одной смертельной ссылки на один xV. Ссылка является смертельной, если она принадлежит стеку временных переменных, одному из множества внутренних стеков perl, который уничтожит эту ссылку «немного позже». Обычно «немного позже» — это конец текущей инструкции Perl. Однако это становится сложнее в динамических областях: могут существовать несколько наборов смертельных ссылок, существующих одновременно с разными датами смерти. Внутри фактический определяющий момент для уничтожения смертельных ссылок на xV зависит от двух макросов, SAVETMPS и FREETMPS. Смотрите perlcall и perlxs и "Temporaries Stack" ниже для получения более подробной информации об этих макросах.

Смертельные ссылки в основном используются для xV, которые помещаются в основной стек perl. Стек проблематичен для отслеживания ссылок, потому что он содержит множество ссылок на xV, но не владеет ими: они не учитываются. В настоящее время существует множество ошибок, возникающих из-за уничтожения xV, в то время как он ссылается на стек, поскольку неучтённые ссылки стека недостаточно для сохранения xV в живых. Поэтому, помещая (не учитываемую) ссылку в стек, крайне важно убедиться, что будет учитываемая ссылка на тот же xV, которая просуществует не менее длительно, чем неучитываемая ссылка. Но также важно, чтобы эта учтённая ссылка была очищена в соответствующее время и не излишне продлевала жизнь xV. Наличие смертельной ссылки часто является лучшим способом удовлетворения этого требования, особенно если xV был создан специально для размещения в стеке и в противном случае был бы неуказанным.

Для создания смертельной ссылки используйте следующие функции:

SV*  sv_newmortal()
SV*  sv_mortalcopy(SV*)
SV*  sv_2mortal(SV*)

sv_newmortal() создаёт SV (со значением undef), единственная ссылка на который смертельна. sv_mortalcopy() создаёт xV, значение которого является копией переданного xV, и единственная ссылка на который смертельна. sv_2mortal() делает существующую ссылку xV смертельной: она передает владение ссылкой от вызывающей стороны стеку временных переменных. Поскольку sv_newmortal не даёт новому SV никакого значения, его обычно следует снабдить значением с помощью sv_setpv, sv_setiv, и т. д.:

SV *tmp = sv_newmortal();
sv_setiv(tmp, an_integer);

Поскольку это несколько инструкций C, довольно часто встречается такой подход:

SV *tmp = sv_2mortal(newSViv(an_integer));

Смертельные процедуры не только для SV; AV и HV могут быть сделаны смертельными путём передачи их адреса (приведённого к типу SV*) в процедуры sv_2mortal или sv_mortalcopy.

Стеки и Глобы

Стек — это хеш, содержащий все переменные, определённые в пакете. Каждый ключ стека — это имя символа (общее для всех разных типов объектов с одинаковым именем), а каждое значение в хеш-таблице — GV (Глобальное значение). Этот GV, в свою очередь, содержит ссылки на различные объекты с этим именем, включая (но не ограничиваясь):

Scalar Value
Array Value
Hash Value
I/O Handle
Format
Subroutine

Существует единственный стек, называемый PL_defstash, который содержит элементы, существующие в пакете main. Чтобы получить доступ к элементам в других пакетах, добавьте строку "::" к имени пакета. Элементы в пакете Foo находятся в стеке Foo:: в PL_defstash. Элементы в пакете Bar::Baz находятся в стеке Baz:: в стеке Bar::.

Чтобы получить указатель на стек для определённого пакета, используйте функцию:

HV*  gv_stashpv(const char* name, I32 flags)
HV*  gv_stashsv(SV*, I32 flags)

Первая функция принимает строку-литерал, вторая использует строку, хранящуюся в SV. Помните, что стек — это всего лишь хеш-таблица, поэтому вы получаете HV*. Флаг flags создаст новый пакет, если он установлен в GV_ADD.

Искомое имя gv_stash*v — это имя пакета, таблицу символов которого вы хотите получить. По умолчанию пакет называется main. Если у вас есть вложенные пакеты, передайте их имена в gv_stash*v, разделяя их ::, как и в языке Perl.

В качестве альтернативы, если у вас есть SV, являющийся благословенной ссылкой, вы можете найти указатель на стек, используя:

HV*  SvSTASH(SvRV(SV*));

затем используйте следующее, чтобы получить само имя пакета:

char*  HvNAME(HV* stash);

Если вам нужно благословить или повторно благословить объект, вы можете использовать следующую функцию:

SV*  sv_bless(SV*, HV* stash)

где первый аргумент, SV*, должен быть ссылкой, а второй аргумент — хранилище. Возвращаемое значение SV* теперь можно использовать так же, как и любой другой SV.

Для получения дополнительной информации о ссылках и благословениях, обратитесь к perlref.

Двухтиповые SV

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

Некоторые переменные скаляров содержат более одного типа данных скаляров. Например, переменная $! содержит либо числовое значение errno, либо его строковое эквивалент из strerror или sys_errlist[].

Чтобы принудительно добавить несколько значений данных в SV, необходимо выполнить два действия: использовать функции sv_set*v для добавления дополнительных типов скаляров, затем установить флаг, чтобы Perl считал, что он содержит более одного типа данных. Четыре макроса для установки флагов:

SvIOK_on
SvNOK_on
SvPOK_on
SvROK_on

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

Например, чтобы создать новую переменную Perl с именем «dberror», которая содержит как числовые, так и описательные строковые значения ошибок, можно использовать следующий код:

extern int  dberror;
extern char *dberror_list;

SV* sv = get_sv("dberror", GV_ADD);
sv_setiv(sv, (IV) dberror);
sv_setpv(sv, dberror_list[dberror]);
SvIOK_on(sv);

Если порядок sv_setiv и sv_setpv был обратным, то нужно было бы вызвать макрос SvPOK_on вместо SvIOK_on.

Значения только для чтения

В Perl 5.16 и более ранних версиях copy-on-write (см. следующий раздел) использовал один и тот же бит флага с скалярами только для чтения. Поэтому единственный способ проверить, sv_setsv, и т. д., приведет к ошибке «Изменение значения только для чтения» в этих версиях:

SvREADONLY(sv) && !SvIsCOW(sv)

В Perl 5.18 и более поздних версиях SvREADONLY применяется только к переменным только для чтения, а в версии 5.20 скаляры copy-on-write также могут быть только для чтения, поэтому приведенная выше проверка неверна. Вам просто нужно:

SvREADONLY(sv)

Если вам нужно часто выполнять эту проверку, определите свой собственный макрос так:

#if PERL_VERSION >= 18
# define SvTRULYREADONLY(sv) SvREADONLY(sv)
#else
# define SvTRULYREADONLY(sv) (SvREADONLY(sv) && !SvIsCOW(sv))
#endif

Копирование при записи

Perl реализует механизм копирования при записи (COW) для скаляров, в котором копии строк не создаются сразу при запросе, а откладываются до тех пор, пока одна из скалярных переменных не изменится. Это в основном прозрачно, но нужно быть осторожным, чтобы не изменять буферы строк, которые совместно используются несколькими SV.

Вы можете проверить, использует ли SV копирование при записи, с помощью SvIsCOW(sv).

Вы можете заставить SV создать собственную копию своего буфера строки, вызвав sv_force_normal(sv) или SvPV_force_nolen(sv).

Если вы хотите удалить буфер строки SV, используйте sv_force_normal_flags(sv, SV_COW_DROP_PV) или просто sv_setsv(sv, NULL).

Все эти функции будут вызывать ошибку при работе со скалярами только для чтения (см. предыдущий раздел для получения дополнительной информации об этом).

Чтобы проверить, что ваш код ведет себя правильно и не изменяет буферы COW, на системах, поддерживающих mmap(2) (т. е. Unix), вы можете сконфигурировать perl с -Accflags=-DPERL_DEBUG_READONLY_COW и он преобразует нарушения буфера в аварийные завершения. Вы обнаружите, что это невероятно медленно, поэтому вы можете пропустить собственные тесты Perl.

Магические переменные

[Этот раздел еще находится в разработке. Проигнорируйте все здесь. Не размещайте объявления. Все, что запрещено, запрещено.]

Любой SV может быть магическим, то есть он обладает особыми функциями, которых нет у обычного SV. Эти функции хранятся в структуре SV в связанном списке struct magic'ов, определённых как MAGIC.

struct magic {
    MAGIC*      mg_moremagic;
    MGVTBL*     mg_virtual;
    U16         mg_private;
    char        mg_type;
    U8          mg_flags;
    I32         mg_len;
    SV*         mg_obj;
    char*       mg_ptr;
};

Обратите внимание, что это актуально на уровне патча 0 и может измениться в любое время.

Назначение магии

Perl добавляет магию к SV с помощью функции sv_magic:

void sv_magic(SV* sv, SV* obj, int how, const char* name, I32 namlen);

Аргумент sv — указатель на SV, которому нужно добавить новую магическую функцию.

Если sv ещё не магический, Perl использует макрос SvUPGRADE для преобразования sv в тип SVt_PVMG. Затем Perl продолжает, добавляя новую магию в начало связанного списка магических функций. Любые предыдущие записи того же типа магии удаляются. Обратите внимание, что это может быть переопределено, и несколько экземпляров одного типа магии могут быть связаны с одним SV.

Аргументы name и namlen используются для связывания строки с магией, обычно имени переменной. namlen хранится в поле mg_len, а если name не равно нулю, то либо копия savepvn name, либо само name хранится в поле mg_ptr, в зависимости от того, больше ли namlen нуля, или равно нулю соответственно. В качестве особого случая, если (name && namlen == HEf_SVKEY), то name предполагается содержащим SV* и хранится как есть с увеличенным значением REFCNT.

Функция sv_magic использует how для определения того, какой, если таковой имеется, предопределённый «магический виртуальный таблицы» должен быть назначен полю mg_virtual . См. раздел "Магические виртуальные таблицы" ниже. Аргумент how также сохраняется в поле mg_type . Значение how должно быть выбрано из набора макросов PERL_MAGIC_foo, присутствующих в perl.h. Обратите внимание, что до добавления этих макросов внутренние части Perl непосредственно использовали символьные литералы, поэтому вы иногда можете встретить старый код или документацию, в которых упоминается магия «U» вместо PERL_MAGIC_uvar и т. п.

Аргумент obj хранится в поле mg_obj структуры MAGIC. Если он не совпадает с аргументом sv, счётчик ссылок объекта obj увеличивается. Если они совпадают, или если аргумент how равен PERL_MAGIC_arylen, PERL_MAGIC_regdatum, PERL_MAGIC_regdata, или если это указатель NULL, то obj просто сохраняется без увеличения счётчика ссылок.

См. также sv_magicext в perlapi для более гибкого способа добавления магии к SV.

Также существует функция для добавления магии к HV:

void hv_magic(HV *hv, GV *gv, int how);

Это просто вызывает sv_magic и принудительно преобразует аргумент gv в SV.

Чтобы удалить магию из SV, вызовите функцию sv_unmagic:

int sv_unmagic(SV *sv, int type);

Аргумент type должен быть равен значению how, когда SV был первоначально сделан магическим.

Однако обратите внимание, что sv_unmagic удаляет всю магию определенного type из SV. Если вы хотите удалить только определённую магию type на основе магической виртуальной таблицы, используйте sv_unmagicext вместо этого:

int sv_unmagicext(SV *sv, int type, MGVTBL *vtbl);

Магические виртуальные таблицы

Поле mg_virtual в структуре MAGIC — указатель на MGVTBL, которая представляет собой структуру указателей на функции и называется «магической виртуальной таблицей» для обработки различных операций, которые могут применяться к этой переменной.

Таблица MGVTBL содержит пять (или иногда восемь) указателей на следующие типы процедур:

int  (*svt_get)  (pTHX_ SV* sv, MAGIC* mg);
int  (*svt_set)  (pTHX_ SV* sv, MAGIC* mg);
U32  (*svt_len)  (pTHX_ SV* sv, MAGIC* mg);
int  (*svt_clear)(pTHX_ SV* sv, MAGIC* mg);
int  (*svt_free) (pTHX_ SV* sv, MAGIC* mg);

int  (*svt_copy) (pTHX_ SV *sv, MAGIC* mg, SV *nsv,
                                      const char *name, I32 namlen);
int  (*svt_dup)  (pTHX_ MAGIC *mg, CLONE_PARAMS *param);
int  (*svt_local)(pTHX_ SV *nsv, MAGIC *mg);

Структура MGVTBL устанавливается во время компиляции в perl.h и в настоящее время содержит 32 типа. Эти разные структуры содержат указатели на различные процедуры, которые выполняют дополнительные действия в зависимости от вызываемой функции.

Function pointer    Action taken
----------------    ------------
svt_get             Do something before the value of the SV is
                    retrieved.
svt_set             Do something after the SV is assigned a value.
svt_len             Report on the SV's length.
svt_clear           Clear something the SV represents.
svt_free            Free any extra storage associated with the SV.

svt_copy            copy tied variable magic to a tied element
svt_dup             duplicate a magic structure during thread cloning
svt_local           copy magic to local value during 'local'

Например, структура MGVTBL, называемая vtbl_sv (которая соответствует mg_type типа PERL_MAGIC_sv), содержит:

{ magic_get, magic_set, magic_len, 0, 0 }

Таким образом, когда SV определяется как магический и типа PERL_MAGIC_sv, если выполняется операция получения, вызывается процедура magic_get. Все различные процедуры для различных магических типов начинаются с magic_. ПРИМЕЧАНИЕ: магические процедуры не считаются частью API Perl и могут не экспортироваться библиотекой Perl.

Последние три слота были добавлены недавно, и для обратной совместимости они проверяются только в том случае, если один из трёх флагов MGf_COPY, MGf_DUP или MGf_LOCAL установлен в mg_flags. Это означает, что большинство кода может продолжать объявлять таблицу v как значение из 5 элементов. В настоящее время эти три используются исключительно кодом потоков и могут существенно измениться.

Текущие типы магических виртуальных таблиц:

mg_type
(old-style char and macro)   MGVTBL         Type of magic
--------------------------   ------         -------------
\0 PERL_MAGIC_sv             vtbl_sv        Special scalar variable
#  PERL_MAGIC_arylen         vtbl_arylen    Array length ($#ary)
%  PERL_MAGIC_rhash          (none)         Extra data for restricted
                                            hashes
*  PERL_MAGIC_debugvar       vtbl_debugvar  $DB::single, signal, trace
                                            vars
.  PERL_MAGIC_pos            vtbl_pos       pos() lvalue
:  PERL_MAGIC_symtab         (none)         Extra data for symbol
                                            tables
<  PERL_MAGIC_backref        vtbl_backref   For weak ref data
@  PERL_MAGIC_arylen_p       (none)         To move arylen out of XPVAV
B  PERL_MAGIC_bm             vtbl_regexp    Boyer-Moore 
                                            (fast string search)
c  PERL_MAGIC_overload_table vtbl_ovrld     Holds overload table 
                                            (AMT) on stash
D  PERL_MAGIC_regdata        vtbl_regdata   Regex match position data 
                                            (@+ and @- vars)
d  PERL_MAGIC_regdatum       vtbl_regdatum  Regex match position data
                                            element
E  PERL_MAGIC_env            vtbl_env       %ENV hash
e  PERL_MAGIC_envelem        vtbl_envelem   %ENV hash element
f  PERL_MAGIC_fm             vtbl_regexp    Formline 
                                            ('compiled' format)
g  PERL_MAGIC_regex_global   vtbl_mglob     m//g target
H  PERL_MAGIC_hints          vtbl_hints     %^H hash
h  PERL_MAGIC_hintselem      vtbl_hintselem %^H hash element
I  PERL_MAGIC_isa            vtbl_isa       @ISA array
i  PERL_MAGIC_isaelem        vtbl_isaelem   @ISA array element
k  PERL_MAGIC_nkeys          vtbl_nkeys     scalar(keys()) lvalue
L  PERL_MAGIC_dbfile         (none)         Debugger %_<filename
l  PERL_MAGIC_dbline         vtbl_dbline    Debugger %_<filename
                                            element
N  PERL_MAGIC_shared         (none)         Shared between threads
n  PERL_MAGIC_shared_scalar  (none)         Shared between threads
o  PERL_MAGIC_collxfrm       vtbl_collxfrm  Locale transformation
P  PERL_MAGIC_tied           vtbl_pack      Tied array or hash
p  PERL_MAGIC_tiedelem       vtbl_packelem  Tied array or hash element
q  PERL_MAGIC_tiedscalar     vtbl_packelem  Tied scalar or handle
r  PERL_MAGIC_qr             vtbl_regexp    Precompiled qr// regex
S  PERL_MAGIC_sig            (none)         %SIG hash
s  PERL_MAGIC_sigelem        vtbl_sigelem   %SIG hash element
t  PERL_MAGIC_taint          vtbl_taint     Taintedness
U  PERL_MAGIC_uvar           vtbl_uvar      Available for use by
                                            extensions
u  PERL_MAGIC_uvar_elem      (none)         Reserved for use by
                                            extensions
V  PERL_MAGIC_vstring        (none)         SV was vstring literal
v  PERL_MAGIC_vec            vtbl_vec       vec() lvalue
w  PERL_MAGIC_utf8           vtbl_utf8      Cached UTF-8 information
x  PERL_MAGIC_substr         vtbl_substr    substr() lvalue
Y  PERL_MAGIC_nonelem        vtbl_nonelem   Array element that does not
                                            exist
y  PERL_MAGIC_defelem        vtbl_defelem   Shadow "foreach" iterator
                                            variable / smart parameter
                                            vivification
\  PERL_MAGIC_lvref          vtbl_lvref     Lvalue reference
                                            constructor
]  PERL_MAGIC_checkcall      vtbl_checkcall Inlining/mutation of call
                                            to this CV
~  PERL_MAGIC_ext            (none)         Available for use by
                                            extensions

Когда в таблице присутствуют как прописная, так и строчная буквы, прописная буква обычно используется для представления какого-либо составного типа (список или хэш), а строчная буква — для представления элемента этого составного типа. Некоторые внутренние части кода используют эту связь по регистру. Однако «v» и «V» (vec и v-строки) никак не связаны.

Магические типы PERL_MAGIC_ext и PERL_MAGIC_uvar определены специально для использования расширениями и не будут использоваться самим Perl. Расширения могут использовать магию PERL_MAGIC_ext для «прикрепления» собственной информации к переменным (обычно к объектам). Это особенно полезно, поскольку обычный код Perl не может повредить эту информацию (в отличие от использования дополнительных элементов объекта хэша).

Аналогично, магия PERL_MAGIC_uvar может использоваться примерно так же, как tie(), для вызова C-функции всякий раз, когда значение скаляра используется или изменяется. Поле MAGIC's mg_ptr указывает на структуру ufuncs:

struct ufuncs {
    I32 (*uf_val)(pTHX_ IV, SV*);
    I32 (*uf_set)(pTHX_ IV, SV*);
    IV uf_index;
};

При чтении или записи SV вызываются функции uf_val или uf_set с uf_index в качестве первого аргумента и указателем на SV во втором. Пример добавления магии PERL_MAGIC_uvar показан ниже. Обратите внимание, что структура ufuncs копируется функцией sv_magic, поэтому её можно безопасно разместить на стеке.

void
Umagic(sv)
    SV *sv;
PREINIT:
    struct ufuncs uf;
CODE:
    uf.uf_val   = &my_get_fn;
    uf.uf_set   = &my_set_fn;
    uf.uf_index = 0;
    sv_magic(sv, 0, PERL_MAGIC_uvar, (char*)&uf, sizeof(uf));

Прикрепление PERL_MAGIC_uvar к массивам разрешено, но не имеет эффекта.

Для хешей существует специализированный обработчик, предоставляющий контроль над ключами хеша (но не над значениями). Этот обработчик вызывает магическую функцию PERL_MAGIC_uvar «get», если функция «set» в структуре ufuncs равна NULL. Обработчик активируется всякий раз, когда к хешу обращаются с ключом, заданным как SV через функции hv_store_ent, hv_fetch_ent, hv_delete_ent, и hv_exists_ent. Обращение к ключу в виде строки через функции без суффикса ..._ent обходит этот обработчик. Подробное описание см. в "GUTS" в Hash::Util::FieldHash.

Обратите внимание, что поскольку несколько расширений могут использовать магию PERL_MAGIC_ext или PERL_MAGIC_uvar, для расширений важно проявлять особую осторожность, чтобы избежать конфликтов. Обычно достаточно использовать магию только для объектов, благословлённых в тот же класс, что и расширение. Для магии PERL_MAGIC_ext обычно рекомендуется определить MGVTBL, даже если все его поля будут 0, чтобы отдельные указатели MAGIC можно было идентифицировать как определённый вид магии, используя их виртуальную таблицу магии. mg_findext предоставляет лёгкий способ сделать это:

STATIC MGVTBL my_vtbl = { 0, 0, 0, 0, 0, 0, 0, 0 };

MAGIC *mg;
if ((mg = mg_findext(sv, PERL_MAGIC_ext, &my_vtbl))) {
    /* this is really ours, not another module's PERL_MAGIC_ext */
    my_priv_data_t *priv = (my_priv_data_t *)mg->mg_ptr;
    ...
}

Также обратите внимание, что функции sv_set*() и sv_cat*() , описанные ранее, не вызывают магическую функцию «set» для своих целевых объектов. Это необходимо делать пользователю, либо вызвав макрос SvSETMAGIC() после вызова этих функций, либо используя одну из функций sv_set*_mg() или sv_cat*_mg(). Аналогично, общий C-код должен вызвать макрос SvGETMAGIC() для вызова любой магической функции «get», если он использует SV, полученный из внешних источников, в функциях, которые не обрабатывают магию. Описание этих функций см. в perlapi. Например, вызовы функций sv_cat*() обычно требуют последующего вызова SvSETMAGIC(), но им не требуется предварительный вызов SvGETMAGIC(), так как их реализация обрабатывает магическую функцию «get».

Поиск магии

MAGIC *mg_find(SV *sv, int type); /* Finds the magic pointer of that
                                   * type */

Эта процедура возвращает указатель на структуру MAGIC, хранящуюся в SV. Если SV не имеет этого магического свойства, возвращается NULL. Если SV имеет несколько экземпляров этого магического свойства, будет возвращён первый из них. mg_findext можно использовать для поиска структуры MAGIC SV, основываясь как на типе магии, так и на её виртуальной таблице:

MAGIC *mg_findext(SV *sv, int type, MGVTBL *vtbl);

Также, если SV, переданный функциям mg_find или mg_findext, не имеет тип SVt_PVMG, Perl может аварийно завершить работу.

int mg_copy(SV* sv, SV* nsv, const char* key, STRLEN klen);

Эта процедура проверяет типы магии, которыми обладает sv. Если поле mg_type содержит заглавную букву, то mg_obj копируется в nsv, но поле mg_type изменяется на строчную букву.

Понимание магии привязанных хешей и массивов

Привязанные хеши и массивы – это магические сущности типа магии PERL_MAGIC_tied.

ПРЕДУПРЕЖДЕНИЕ: По состоянию на релиз 5.004, для правильного использования функций доступа к массивам и хешам необходимо учитывать несколько нюансов. Некоторые из этих нюансов на самом деле считаются ошибками в API, которые будут исправлены в будущих версиях, и выделены в квадратных скобках [MAYCHANGE] ниже. Если вы обнаружите применение такой информации в этом разделе, имейте в виду, что поведение может измениться в будущем, эмм, без предупреждения.

Функция perl tie связывает переменную с объектом, реализующим различные методы GET, SET и т. д. Чтобы выполнить эквивалент функции perl tie из XSUB, необходимо имитировать это поведение. Приведенный ниже код выполняет необходимые шаги — во-первых, он создаёт новый хеш, затем создаёт второй хеш, который благословляет в класс, который будет реализовывать методы привязки. Наконец, он связывает два хеша вместе и возвращает ссылку на новый привязанный хеш. Обратите внимание, что приведенный ниже код НЕ вызывает метод TIEHASH в классе MyTie – см. "Вызов Perl-функций из C-программ" для получения подробностей о том, как это сделать.

SV*
mytie()
PREINIT:
    HV *hash;
    HV *stash;
    SV *tie;
CODE:
    hash = newHV();
    tie = newRV_noinc((SV*)newHV());
    stash = gv_stashpv("MyTie", GV_ADD);
    sv_bless(tie, stash);
    hv_magic(hash, (GV*)tie, PERL_MAGIC_tied);
    RETVAL = newRV_noinc(hash);
OUTPUT:
    RETVAL

Функция av_store, когда ей передаётся привязанный массив в качестве аргумента, просто копирует магию массива на значение, которое должно быть "сохранено", используя mg_copy. Она также может вернуть NULL, указывая, что значение фактически не нужно было сохранять в массиве. [MAYCHANGE] После вызова av_store на привязанном массиве вызывающий обычно должен вызвать mg_set(val) для фактического вызова метода perl "STORE" на объекте TIEARRAY. Если av_store вернула NULL, вызов SvREFCNT_dec(val) также будет обычно необходим, чтобы избежать утечки памяти. [/MAYCHANGE]

Предыдущий абзац полностью применим к доступу к привязанному хешу с использованием функций hv_store и hv_store_ent.

Функции av_fetch и соответствующие функции для хешей hv_fetch и hv_fetch_ent фактически возвращают неопределённое смертное значение, чья магия была инициализирована с помощью mg_copy. Обратите внимание, что возвращаемое значение не нужно освобождать, так как оно уже смертное. [MAYCHANGE] Но вам необходимо вызвать mg_get() на возвращаемом значении, чтобы фактически вызвать метод perl "FETCH" на базовом объекте TIE. Аналогично, вы можете также вызвать mg_set() на возвращаемом значении после возможного присваивания подходящего значения с помощью sv_setsv, что вызовет метод "STORE" на объекте TIE. [/MAYCHANGE]

[MAYCHANGE] Другими словами, функции извлечения/хранения массивов или хешей на самом деле не извлекают и не сохраняют фактические значения в случае привязанных массивов и хешей. Они просто вызывают mg_copy для добавления магии к значениям, которые должны были быть "сохранены" или "извлечены". Позже вызовы mg_get и mg_set фактически выполняют работу по вызову методов TIE на базовых объектах. Таким образом, механизм магии в настоящее время реализует некую ленивую модель доступа к массивам и хешам.

В настоящее время (по состоянию на версию Perl 5.004) использование функций доступа к хешам и массивам требует от пользователя понимания, работают ли они с "обычными" хешами и массивами или с их привязанными аналогами. API может быть изменён для обеспечения более прозрачного доступа к привязанным и обычным типам данных в будущих версиях. [/MAYCHANGE]

Вам следует понимать, что интерфейсы TIEARRAY и TIEHASH — это просто удобный синтаксис для вызова некоторых вызовов Perl-методов при использовании единообразного синтаксиса хешей и массивов. Использование этого синтаксиса вносит некоторый накладной расход (обычно от двух до четырёх дополнительных команд оператора за операцию FETCH/STORE в дополнение к созданию всех необходимых смертных переменных для вызова методов). Этот накладной расход будет сравнительно невелик, если методы TIE сами по себе объёмны, но если они содержат лишь несколько инструкций, накладной расход не будет незначительным.

Локализация изменений

Perl имеет очень удобную конструкцию

{
  local $var = 2;
  ...
}

Эта конструкция приблизительно эквивалентна

{
  my $oldvar = $var;
  $var = 2;
  ...
  $var = $oldvar;
}

Главное отличие заключается в том, что первая конструкция восстановит начальное значение $var, независимо от того, как будет завершено выполнение блока: goto, return, die/eval, и т.д. Она также немного эффективнее.

Существует способ достижения аналогичного результата из C через Perl API: создать псевдо-блок и организовать автоматическое отмену некоторых изменений в конце блока, либо явным образом, либо при выходе из блока с помощью нелокального выхода (например, с помощью die()). Блок-подобная конструкция создаётся с помощью пары макросов ENTER/LEAVE (см. "Возвращение скаляра" в perlcall). Такая конструкция может быть создана специально для некоторого важного локального задания или может быть использована существующая (например, границы вложенной Perl-подпрограммы/блока или существующая пара для освобождения TMP). (В последнем случае накладные расходы дополнительной локализации должны быть почти незаметны.) Обратите внимание, что любой XSUB автоматически заключён в пару ENTER/LEAVE.

Внутри такого псевдо-блока доступна следующая услуга:

SAVEINT(int i)
SAVEIV(IV i)
SAVEI32(I32 i)
SAVELONG(long i)

Эти макросы организуют сохранение значения целочисленной переменной i в конце окружающего псевдоблока.

SAVESPTR(s)
SAVEPPTR(p)

Эти макросы организуют сохранение значений указателей s и p. s должен быть указателем типа, который выдерживает преобразование в SV* и обратно, а p должен выдерживать преобразование в char* и обратно.

SAVEFREESV(SV *sv)

Счётчик ссылок sv будет уменьшен в конце псевдоблока. Это похоже на sv_2mortal, поскольку это также механизм для выполнения отложенного SvREFCNT_dec. Однако, в то время как sv_2mortal продлевает срок жизни sv до начала следующей инструкции, SAVEFREESV продлевает его до конца окружающего области видимости. Эти сроки жизни могут сильно различаться.

Также сравните SAVEMORTALIZESV.

SAVEMORTALIZESV(SV *sv)

Точно так же, как и SAVEFREESV, но смертельно делает sv в конце текущей области видимости вместо уменьшения счётчика ссылок. Обычно это приводит к тому, что sv остаётся живым до тех пор, пока инструкция, вызвавшая текущую область видимости, не завершит выполнение.

SAVEFREEOP(OP *op)

OP * освобождается с помощью op_free() в конце псевдоблока.

SAVEFREEPV(p)

Блок памяти, на который указывает p, освобождается с помощью Safefree() в конце псевдоблока.

SAVECLEARSV(SV *sv)

Очищает ячейку в текущем наборе временных данных, которая соответствует sv в конце псевдоблока.

SAVEDELETE(HV *hv, char *key, I32 length)

Ключ key словаря hv удаляется в конце псевдоблока. Строка, на которую указывает key, освобождается с помощью Safefree(). Если у вас есть ключ в хранилище с коротким сроком жизни, соответствующая строка может быть перераспределена следующим образом:

SAVEDELETE(PL_defstash, savepv(tmpbuf), strlen(tmpbuf));
SAVEDESTRUCTOR(DESTRUCTORFUNC_NOCONTEXT_t f, void *p)

В конце псевдоблока вызывается функция f с единственным аргументом p.

SAVEDESTRUCTOR_X(DESTRUCTORFUNC_t f, void *p)

В конце псевдоблока вызывается функция f с неявным аргументом контекста (если таковой имеется) и p.

SAVESTACK_POS()

Текущее смещение на внутреннем Perl-стеке (см. SP) восстанавливается в конце псевдоблока.

Следующий список API содержит функции, поэтому необходимо явно указать указатели на изменяемые данные (либо указатели C, либо Perl'ские GV *). В тех случаях, где вышеприведённые макросы принимают int, аналогичная функция принимает int *.

SV* save_scalar(GV *gv)

Эквивалентно коду Perl local $gv.

AV* save_ary(GV *gv)
HV* save_hash(GV *gv)

Аналогично save_scalar, но локализует @gv и %gv.

void save_item(SV *item)

Создаёт копию текущего значения SV. При выходе из текущего псевдоблока ENTER/LEAVE значение SV будет восстановлено с помощью сохранённого значения. Он не обрабатывает магию. Используйте save_scalar если магию нужно учитывать.

void save_list(SV **sarg, I32 maxsarg)

Вариант save_item, принимающий несколько аргументов через массив sarg из SV* длиной maxsarg.

SV* save_svref(SV **sptr)

Аналогично save_scalar, но восстановит SV *.

void save_aptr(AV **aptr)
void save_hptr(HV **hptr)

Аналогично save_svref, но локализует AV * и HV *.

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

Подпрограммы

XSUB и стек аргументов

Механизм XSUB — это простой способ для программ Perl доступа к C-подпрограммам. Подпрограмма XSUB будет иметь стек, содержащий аргументы из программы Perl, и способ отображения данных Perl на C-эквиваленты.

Аргументы стека доступны через макрос ST(n), который возвращает n-й аргумент стека. Аргумент 0 — это первый аргумент, переданный при вызове подпрограммы Perl. Эти аргументы являются SV*, и их можно использовать везде, где используется SV*.

Большинство раз, результаты работы C-подпрограммы можно обработать с помощью директив RETVAL и OUTPUT. Однако существуют случаи, когда стека аргументов недостаточно для обработки всех возвращаемых значений. Примером является вызов POSIX tzname(), который не принимает аргументы, но возвращает два — стандартное и летнее время местного часового пояса.

Для обработки этой ситуации используется директива PPCODE, и стек расширяется с помощью макроса:

EXTEND(SP, num);

где SP — это макрос, представляющий локальную копию указателя стека, а num — число элементов, на которое должен быть расширен стек.

Теперь, когда на стеке есть место, значения можно поместить на него с помощью макроса PUSHs . Помещаемые значения часто должны быть «смертными» (См. "Счётчики ссылок и смертность"):

PUSHs(sv_2mortal(newSViv(an_integer)))
PUSHs(sv_2mortal(newSVuv(an_unsigned_integer)))
PUSHs(sv_2mortal(newSVnv(a_double)))
PUSHs(sv_2mortal(newSVpv("Some String",0)))
/* Although the last example is better written as the more
 * efficient: */
PUSHs(newSVpvs_flags("Some String", SVs_TEMP))

И теперь, в программе Perl, вызывающей tzname, два значения будут присвоены так же, как и в:

($standard_abbrev, $summer_abbrev) = POSIX::tzname;

Альтернативный (и, возможно, более простой) метод помещения значений в стек — использование макроса:

XPUSHs(SV*)

Этот макрос автоматически корректирует стек при необходимости. Таким образом, вам не нужно вызывать EXTEND для расширения стека.

Несмотря на свои указания в более ранних версиях этого документа, макросы (X)PUSH[iunp] не подходят для XSUB-подпрограмм, возвращающих несколько результатов. В этом случае нужно либо придерживаться макросов (X)PUSHs выше, либо использовать новые макросы m(X)PUSH[iunp]; см. "Помещение C-значения на Perl-стек".

Для получения дополнительной информации, обратитесь к perlxs и perlxstut.

Автозагрузка с XSUB

Если подпрограмма AUTOLOAD является XSUB, как и в случае с подпрограммами Perl, Perl помещает полное имя подпрограммы autoload в переменную $AUTOLOAD пакета XSUB.

Но также она помещает ту же информацию в определённые поля самого XSUB:

HV *stash           = CvSTASH(cv);
const char *subname = SvPVX(cv);
STRLEN name_length  = SvCUR(cv); /* in bytes */
U32 is_utf8         = SvUTF8(cv);

SvPVX(cv) содержит только само имя подпрограммы, без указания пакета. Для подпрограммы AUTOLOAD в UNIVERSAL или одном из его суперклассов, CvSTASH(cv) возвращает NULL во время вызова метода для несуществующего пакета.

Примечание: Установка $AUTOLOAD перестала работать в 5.6.1, которая не поддерживала XS AUTOLOAD-подпрограммы. Perl 5.8.0 ввёл использование полей в самом XSUB. Perl 5.16.0 восстановил установку $AUTOLOAD. Если вам нужно поддерживать версии 5.8-5.14, используйте поля XSUB.

Вызов Perl-подпрограмм из C-программ

Существует четыре подпрограммы, которые можно использовать для вызова Perl-подпрограммы из C-программы:

I32  call_sv(SV*, I32);
I32  call_pv(const char*, I32);
I32  call_method(const char*, I32);
I32  call_argv(const char*, I32, char**);

Подпрограмма, которая чаще всего используется, это call_sv. Аргумент SV* содержит либо имя Perl-подпрограммы, которую нужно вызвать, либо ссылку на подпрограмму. Второй аргумент состоит из флагов, которые управляют контекстом, в котором вызывается подпрограмма, передаются ли аргументы подпрограмме, как обрабатывать ошибки и как обрабатывать возвращаемые значения.

Все четыре подпрограммы возвращают количество аргументов, возвращённых подпрограммой на Perl-стеке.

Эти подпрограммы раньше назывались perl_call_sv, и т. д., до Perl v5.6.0, но эти имена теперь устарели; для совместимости предоставляются макросы с теми же именами.

При использовании любой из этих подпрограмм (кроме call_argv ), программист должен манипулировать Perl-стеком. Это включает следующие макросы и функции:

dSP
SP
PUSHMARK()
PUTBACK
SPAGAIN
ENTER
SAVETMPS
FREETMPS
LEAVE
XPUSH*()
POP*()

Для подробного описания соглашений вызова из C в Perl, обратитесь к perlcall.

Помещение C-значения на Perl-стек

Многие команды (это элементарная операция во внутренней машине Perl-стека) помещают SV* на стек. Однако в целях оптимизации соответствующий SV (обычно) не воссоздаётся каждый раз. Команды повторно используют специально назначенные SV (цели), которые (как следствие) не постоянно освобождаются/создаются.

Каждая цель создаётся только один раз (но см. "Наборы временных данных и рекурсия" ниже), и когда команде необходимо поместить целое число, двойное значение или строку в стек, она просто устанавливает соответствующие части своей цели и помещает цель в стек.

Макрос для помещения этой цели в стек — PUSHTARG, и он напрямую используется в некоторых командах, а также косвенно во множестве других, которые используют его через (X)PUSH[iunp].

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

XPUSHi(10);
XPUSHi(20);

Это переводится как «установить TARG в 10, поместить указатель на TARG в стек; установить TARG в 20, поместить указатель на TARG в стек». В конце операции в стеке не содержится значений 10 и 20, а фактически содержатся два указателя на TARG, которые мы установили в 20.

Если вам нужно передать несколько разных значений, то вы должны использовать макросы (X)PUSHs или новые макросы m(X)PUSH[iunp], ни один из которых не использует TARG. Макросы (X)PUSHs просто помещают SV* в стек, который, как указано в "XSUB и стек аргументов", часто должен быть «временным». Новые макросы m(X)PUSH[iunp] облегчают это, создавая для вас новый временный объект (через (X)PUSHmortal), помещая его в стек (расширяя его при необходимости, как в случае с макросами mXPUSH[iunp]), и затем устанавливая его значение. Таким образом, вместо того, чтобы писать это, чтобы «исправить» пример выше:

XPUSHs(sv_2mortal(newSViv(10)))
XPUSHs(sv_2mortal(newSViv(20)))

вы можете просто написать:

mXPUSHi(10)
mXPUSHi(20)

Относительно, если вы используете (X)PUSH[iunp], вам потребуется dTARG в объявлениях переменных, чтобы макросы *PUSH* могли использовать локальную переменную TARG. См. также dTARGET и dXSTARG.

Справочные области

Вопрос остается о том, когда создаются SV, являющиеся целями для операций. Ответ заключается в том, что они создаются во время компиляции текущего блока — подпрограммы или файла (для операций над операторами вне подпрограмм). В это время создается специальный анонимный массив Perl, который называется справочной областью для текущего блока.

Справочная область хранит SV, которые являются лексическими переменными для текущего блока и являются целями для операций. Предыдущая версия этого документа утверждала, что можно определить, что SV находится в справочной области, посмотрев на его флаги: лексические переменные имеют флаг SVs_PADMY установленным, а цели имеют флаг SVs_PADTMP установленным. Но это никогда не было полностью верным. Флаг SVs_PADMY может быть установлен для переменной, которая больше не находится в какой-либо области. Хотя у целей установлен флаг SVs_PADTMP, он также может быть установлен для переменных, которые никогда не находились в области, но тем не менее действуют как цели. Начиная с perl 5.21.5, флаг SVs_PADMY больше не используется и определен как 0. SvPADMY() теперь возвращает true для всего, у чего нет флага SVs_PADTMP.

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

Справочные области и рекурсия

На самом деле, не совсем верно, что скомпилированный блок содержит указатель на массив справочных областей AV. Фактически он содержит указатель на массив AV (вначале) из одного элемента, и этот элемент — массив справочных областей AV. Зачем нам нужен дополнительный уровень косвенности?

Ответ — рекурсия, и, возможно, потоки. Оба могут создавать несколько указателей на выполнение, входящих в одну и ту же подпрограмму. Чтобы подпрограмма-потомок не перезаписывала временные переменные подпрограммы-родителя (срок жизни которой охватывает вызов потомка), родитель и потомок должны иметь разные справочные области. (И лексические переменные должны быть отдельными!).

Итак, каждая подпрограмма рождается с массивом справочных областей (длиной 1). При каждом входе в подпрограмму проверяется, что текущая глубина рекурсии не превышает длину этого массива, и если превышает, создается новая справочная область и добавляется в массив.

Целями в этой справочной области являются undef, но они уже помечены правильными флагами.

Распределение памяти

Распределение

Вся память, предназначенная для использования с функциями API Perl, должна обрабатываться с помощью макросов, описанных в этом разделе. Макросы обеспечивают необходимую прозрачность различий в фактической реализации malloc, используемой в Perl.

Следующие три макроса используются для первоначального выделения памяти:

Newx(pointer, number, type);
Newxc(pointer, number, type, cast);
Newxz(pointer, number, type);

Первый аргумент pointer должен быть именем переменной, которая будет указывать на выделенную память.

Второй и третий аргументы number и type указывают, сколько структур указанного типа должно быть выделено. Аргумент type передается в sizeof. Последний аргумент к Newxc, cast, должен использоваться, если аргумент pointer отличается от аргумента type.

В отличие от макросов Newx и Newxc, макрос Newxz вызывает memzero для обнуления всей выделенной памяти.

Перераспределение

Renew(pointer, number, type);
Renewc(pointer, number, type, cast);
Safefree(pointer)

Эти три макроса используются для изменения размера буфера памяти или для освобождения блока памяти, который больше не нужен. Аргументы к Renew и Renewc соответствуют аргументам New и Newc, за исключением того, что не требуется аргумент «магическое число».

Перемещение

Move(source, dest, number, type);
Copy(source, dest, number, type);
Zero(dest, number, type);

Эти три макроса используются для перемещения, копирования или обнуления ранее выделенной памяти. Аргументы source и dest указывают начальные точки источника и назначения. Perl переместит, скопирует или обнулит number экземпляров размера структуры данных type (используя функцию sizeof).

PerlIO

Последние версии Perl экспериментируют с удалением зависимости Perl от стандартной системы ввода-вывода и позволяют использовать другие реализации stdio. Это подразумевает создание нового уровня абстракции, который затем вызывает ту реализацию stdio, с которой был скомпилирован Perl. Все XSUBы должны теперь использовать функции уровня абстракции PerlIO, а не делать никаких предположений о типе используемого stdio.

Для получения подробного описания абстракции PerlIO см. perlapio.

Скомпилированный код

Дерево кода

Здесь мы описываем внутреннюю форму, в которую преобразуется ваш код Perl. Начнем с простого примера:

$a = $b + $c;

Это преобразуется в дерево, подобное этому:

       assign-to
     /           \
    +             $a
  /   \
$b     $c

(но немного сложнее). Это дерево отражает способ, которым Perl проанализировал ваш код, но не имеет ничего общего с порядком выполнения. Существует дополнительный «поток», проходящий через узлы дерева, который показывает порядок выполнения узлов. В нашем упрощенном примере выше он выглядит так:

$b ---> $c ---> + ---> $a ---> assign-to

Но с фактическим деревом компиляции для $a = $b + $c это отличается: некоторые узлы оптимизированы. Как следствие, хотя фактическое дерево содержит больше узлов, чем наш упрощенный пример, порядок выполнения такой же, как в нашем примере.

Просмотр дерева

Если у вас Perl скомпилирован для отладки (обычно это делается с помощью -DDEBUGGING в командной строке Configure), вы можете просмотреть скомпилированное дерево, указав -Dx в командной строке Perl. Вывод занимает несколько строк на узел, и для $b+$c он выглядит так:

5           TYPE = add  ===> 6
            TARG = 1
            FLAGS = (SCALAR,KIDS)
            {
                TYPE = null  ===> (4)
                  (was rv2sv)
                FLAGS = (SCALAR,KIDS)
                {
3                   TYPE = gvsv  ===> 4
                    FLAGS = (SCALAR)
                    GV = main::b
                }
            }
            {
                TYPE = null  ===> (5)
                  (was rv2sv)
                FLAGS = (SCALAR,KIDS)
                {
4                   TYPE = gvsv  ===> 5
                    FLAGS = (SCALAR)
                    GV = main::c
                }
            }

Это дерево имеет 5 узлов (по одному на каждый спецификатор TYPE ), только 3 из них не оптимизированы (по одному на каждое число в левом столбце). Непосредственные потомки данного узла соответствуют парам {} на одном уровне вложенности, таким образом, этот список соответствует дереву:

    add
  /     \
null    null
 |       |
gvsv    gvsv

Порядок выполнения указан метками ===>, следовательно, он равен 3 4 5 6 (узел 6 не включен в вышеприведенный список), т.е., gvsv gvsv add whatever.

Каждый из этих узлов представляет собой операцию (op) — фундаментальную операцию внутри ядра Perl. Код, реализующий каждую операцию, можно найти в файлах pp*.c; функция, реализующая операцию с типом gvsv, — это pp_gvsv, и так далее. Как показывает дерево выше, у разных операций разное количество потомков: add — это бинарный оператор, как можно было ожидать, и у него, следовательно, два потомка. Чтобы учесть различные количества потомков, существуют различные типы структур данных операторов, и они связаны по-разному.

Простейший тип структуры оператора — OP; у него нет потомков. Унарные операторы, UNOP, имеют одного потомка, и на него указывает поле op_first . У бинарных операторов (BINOP) есть не только поле op_first, но и поле op_last. Наиболее сложный тип оператора — LISTOP, который имеет любое количество потомков. В этом случае первый потомок указывается полем op_first, а последний — полем op_last. Потомки между ними можно найти, итеративно следуя указателю OpSIBLING от первого потомка до последнего (но см. ниже).

Также существуют и другие типы операторов: PMOP содержит регулярное выражение и не имеет потомков, а LOOP может или не может иметь потомков. Если поле op_children отлично от нуля, оно ведёт себя как LISTOP. Чтобы усложнить ситуацию, если UNOP на самом деле является оператором null после оптимизации (см. "Компиляция проход 2: распространение контекста"), у него по-прежнему будут потомки в соответствии с его прежним типом.

Наконец, есть оператор логики LOGOP. Как и у LISTOP, у него есть один или несколько потомков, но нет поля op_last: вам нужно следовать по цепочке op_first и потом по цепочке OpSIBLING для поиска последнего потомка. Вместо этого у него есть поле op_other, которое сравнимо с полем op_next, описанным ниже, и представляет собой альтернативный путь выполнения. Операторы, такие как and, or и ?, являются LOGOP. Обратите внимание, что в общем случае op_other может не указывать на любого из прямых потомков LOGOP.

Начиная с версии 5.21.2, в перлах, скомпилированных с экспериментальным определением -DPERL_OP_PARENT, добавляется дополнительный булевый флаг для каждой операции, op_moresib. Если он не установлен, это означает, что это последняя операция в цепочке OpSIBLING. Это освобождает поле op_sibling последнего потомка для указания на родительскую операцию. В этой сборке поле также переименовано в op_sibparent , чтобы отразить его двойную роль. Макрос OpSIBLING(o) обрабатывает это специальное поведение и всегда возвращает NULL для последнего потомка. В этой сборке функция op_parent(o) может использоваться для поиска родителя любой операции. Таким образом, для обратной совместимости вы всегда должны использовать макрос OpSIBLING(o), а не обращаться к op_sibling напрямую.

Другой способ просмотра дерева — использование модуля компилятора back-end, такого как B::Concise.

Компиляция проход 1: процедуры проверки

Дерево создаётся компилятором, в то время как код yacc предоставляет ему конструкции, которые он распознаёт. Поскольку yacc работает по принципу «снизу вверх», то и первый проход компиляции perl работает аналогично.

То, что делает этот проход интересным для разработчиков perl, заключается в возможности выполнения некоторой оптимизации на этом этапе. Это оптимизация с помощью так называемых «программ проверки». Соответствие между именами узлов и соответствующими программами проверки описано в файле opcode.pl (не забудьте выполнить make regen_headers, если вы изменяете этот файл).

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

Программа проверки возвращает узел, который должен быть вставлен в дерево (если узел верхнего уровня не был изменён, программа проверки возвращает свой аргумент).

Согласно соглашению, программы проверки имеют имена ck_*. Обычно они вызываются из подпрограмм new*OP (или convert) (которые, в свою очередь, вызываются из perly.y).

Проход компиляции 1a: свёртка констант

Сразу после вызова программы проверки возвращённый узел проверяется на возможность выполнения во время компиляции. Если это возможно (значение считается константой), оно выполняется немедленно, и вместо него подставляется узел constant со «значением возврата» соответствующего поддерева. Поддерево удаляется.

Если свёртка констант не была выполнена, создаётся поток порядка выполнения.

Проход компиляции 2: распространение контекста

Когда контекст для части дерева компиляции известен, он распространяется по дереву вниз. В этот момент контекст может иметь 5 значений (вместо 2 для контекста времени выполнения): void, boolean, scalar, list и lvalue. В отличие от прохода 1, этот проход обрабатывается сверху вниз: контекст узла определяет контекст для его дочерних узлов.

В это время выполняются дополнительные оптимизации, зависящие от контекста. Поскольку в этом момент дерево компиляции содержит обратные ссылки (через указатели «потока»), узлы сейчас не могут быть освобождены (free()). Для того, чтобы оптимизированные узлы могли быть удалены на этом этапе, такие узлы вместо освобождения (free()) обнуляются (null()ified), то есть их тип изменяется на OP_NULL.

Проход компиляции 3: оптимизация по околу

После создания дерева компиляции для подпрограммы (или для eval или файла) выполняется дополнительный проход по коду. Этот проход не является ни сверху вниз, ни снизу вверх, а идёт по порядку выполнения (с дополнительными осложнениями для условных операторов). Оптимизации, выполняемые на этом этапе, подчиняются тем же ограничениям, что и на проходе 2.

Оптимизации по околу выполняются путём вызова функции, на которую указывает глобальная переменная PL_peepp. По умолчанию, PL_peepp просто вызывает функцию, на которую указывает глобальная переменная PL_rpeepp. По умолчанию, эта функция выполняет некоторые базовые исправления и оптимизации вдоль цепочки операторов порядка выполнения и рекурсивно вызывает PL_rpeepp для каждой побочной цепочки операторов (возникающих из условных операторов). Расширения могут предоставить дополнительные оптимизации или исправления, подключаясь либо к стадии по подпрограммам, либо к рекурсивной стадии, например так:

static peep_t prev_peepp;
static void my_peep(pTHX_ OP *o)
{
    /* custom per-subroutine optimisation goes here */
    prev_peepp(aTHX_ o);
    /* custom per-subroutine optimisation may also go here */
}
BOOT:
    prev_peepp = PL_peepp;
    PL_peepp = my_peep;

static peep_t prev_rpeepp;
static void my_rpeep(pTHX_ OP *o)
{
    OP *orig_o = o;
    for(; o; o = o->op_next) {
        /* custom per-op optimisation goes here */
    }
    prev_rpeepp(aTHX_ orig_o);
}
BOOT:
    prev_rpeepp = PL_rpeepp;
    PL_rpeepp = my_rpeep;

Подключаемые runops

Дерево компиляции выполняется в функции runops. Существуют две функции runops, в run.c и в dump.c. Perl_runops_debug используется с отладкой, а Perl_runops_standard используется в противном случае. Для тонкого управления выполнением дерева компиляции можно предоставить свою собственную функцию runops.

Вероятно, лучше скопировать одну из существующих функций runops и изменить её в соответствии со своими потребностями. Затем в разделе BOOT вашего файла XS добавьте строку:

PL_runops = my_runops;

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

Обработчики области видимости во время компиляции

Начиная с perl 5.14, можно подключиться к механизму лексической области видимости во время компиляции, используя Perl_blockhook_register. Это используется так:

STATIC void my_start_hook(pTHX_ int full);
STATIC BHK my_hooks;

BOOT:
    BhkENTRY_set(&my_hooks, bhk_start, my_start_hook);
    Perl_blockhook_register(aTHX_ &my_hooks);

Это организует вызов my_start_hook в начале компиляции каждой лексической области видимости. Доступные обработчики:

void bhk_start(pTHX_ int full)

Этот обработчик вызывается сразу после начала новой лексической области видимости. Обратите внимание, что Perl-код, подобный

if ($x) { ... }

создаёт две области видимости: первая начинается в ( и имеет full == 1, вторая начинается в { и имеет full == 0. Обе заканчиваются в }, поэтому вызовы start и pre/post_end будут соответствовать. Всё, что было помещено на стек сохранения этим обработчиком, будет извлечено непосредственно перед завершением области видимости (между обработчиками pre_ и post_end).

void bhk_pre_end(pTHX_ OP **o)

Этот обработчик вызывается в конце лексической области видимости, непосредственно перед разворачиванием стека. o — корень дерева optree, представляющего область видимости; это указатель на указатель, поэтому вы можете заменить OP, если нужно.

void bhk_post_end(pTHX_ OP **o)

Этот обработчик вызывается в конце лексической области видимости, непосредственно после разворачивания стека. o — как выше. Обратите внимание, что вызовы pre_ и post_end могут быть вложенными, если на стеке сохранения есть что-то, вызывающее строковый eval.

void bhk_eval(pTHX_ OP *const o)

Этот обработчик вызывается непосредственно перед началом компиляции eval STRING, do FILE, require или use, после того, как eval был настроен. o — OP, который запросил eval, и обычно будет OP_ENTEREVAL, OP_DOFILE или OP_REQUIRE.

После того, как у вас есть функции обработчиков, вам нужна структура BHK для их размещения. Лучше всего выделить её статически, поскольку её нельзя освободить после регистрации. Указатели на функции следует вставлять в эту структуру с помощью макроса BhkENTRY_set, который также установит флаги, указывающие, какие записи действительны. Если вам всё же нужно динамически выделить вашу BHK по какой-то причине, убедитесь, что она обнулена перед началом.

После регистрации нет механизма для отключения этих обработчиков, поэтому, если это необходимо, вам придётся сделать это самостоятельно. Запись в %^H является, вероятно, лучшим способом, так что эффект ограничен лексической областью видимости; однако также возможно использовать макросы BhkDISABLE и BhkENABLE для временного включения и выключения записей. Вы также должны помнить, что, как правило, по крайней мере одна область видимости откроется до загрузки вашего расширения, поэтому вы увидите некоторые пары pre/post_end, у которых не было соответствующего start.

Просмотр внутренних структур данных с помощью функций dump

Для помощи в отладке исходный файл dump.c содержит ряд функций, которые производят отформатированный вывод внутренних структур данных.

Наиболее часто используемая из этих функций — Perl_sv_dump; она используется для вывода SVs, AVs, HVs и CVs. Модуль Devel::Peek вызывает sv_dump для создания отладочного вывода из Perl-пространства, поэтому пользователи этого модуля уже должны быть знакомы с его форматом.

Perl_op_dump может использоваться для вывода структуры OP или любого её производного, и выводит вывод, аналогичный perl -Dx; на самом деле, Perl_dump_eval выведет основной корень кода, который оценивается, точно так же, как -Dx.

Другие полезные функции — Perl_dump_sub, которая преобразует GV в дерево операторов, Perl_dump_packsubs, которая вызывает Perl_dump_sub для всех подпрограмм в пакете, например так: (К счастью, все это xsubs, поэтому нет дерева операторов)

(gdb) print Perl_dump_packsubs(PL_defstash)

SUB attributes::bootstrap = (xsub 0x811fedc 0)

SUB UNIVERSAL::can = (xsub 0x811f50c 0)

SUB UNIVERSAL::isa = (xsub 0x811f304 0)

SUB UNIVERSAL::VERSION = (xsub 0x811f7ac 0)

SUB DynaLoader::boot_DynaLoader = (xsub 0x805b188 0)

и Perl_dump_all, которая выводит все подпрограммы в хранилище и дерево операторов основного корня.

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

Предыстория и PERL_IMPLICIT_CONTEXT

Интерпретатор Perl можно рассматривать как закрытый ящик: он имеет API для подачи в него кода или для выполнения других действий, но также имеет функции для собственного использования. Это очень похоже на объект, и есть способы построения Perl таким образом, чтобы у вас было несколько интерпретаторов, причём каждый интерпретатор представлен либо как структура C, либо внутри специфичной для потока структуры. Эти структуры содержат весь контекст, состояние этого интерпретатора.

Один макрос управляет основным вкусом сборки Perl: MULTIPLICITY. Сборка MULTIPLICITY имеет структуру C, которая упаковывает всё состояние интерпретатора. В многоинтерпретаторных perl, PERL_IMPLICIT_CONTEXT обычно также определён и позволяет передавать «скрытый» первый аргумент, который представляет все три структуры данных. MULTIPLICITY делает многопоточные perl возможными (с моделью потоков ithreads, связанной с макросом USE_ITHREADS.)

Два других «упаковывающих» макроса — PERL_GLOBAL_STRUCT и PERL_GLOBAL_STRUCT_PRIVATE (последний включает первый, а первый включает MULTIPLICITY). PERL_GLOBAL_STRUCT заставляет все внутренние переменные Perl быть заключёнными в одну глобальную структуру, struct perl_vars, доступную как (globals) &PL_Vars или PL_VarsPtr или функцию Perl_GetVars(). PERL_GLOBAL_STRUCT_PRIVATE идёт ещё дальше, всё ещё есть одна структура (выделенная в main() либо из кучи, либо из стека), но нет глобальных символов данных, указывающих на неё. В любом случае глобальная структура должна быть инициализирована как самая первая вещь в main() с помощью Perl_init_global_struct() и соответственно разрушена после perl_free() с помощью Perl_free_global_struct(), пожалуйста, смотрите miniperlmain.c для деталей использования. Вам также может потребоваться использовать dVAR в вашем коде, чтобы «объявить глобальные переменные», когда вы их используете. dTHX делает это за вас автоматически.

Чтобы проверить, есть ли у вас данные, не являющиеся константами, вы можете использовать совместимый с BSD (или GNU) nm.

nm libperl.a | grep -v ' [TURtr] '

Если это отображает какие-либо D или d символы (или, возможно, C или c), у вас есть данные, не являющиеся константами. Символы, которые grep удалил, таковы: Tt — это текст или код, Rr — только для чтения (const) данные, а U — <undefined>, внешние символы, к которым есть ссылки.

Тест t/porting/libperl.t выполняет подобную проверку целостности символов на libperl.a.

END_OF_DOCUMENT_MARKER

По причинам обратной совместимости определение только PERL_GLOBAL_STRUCT фактически не скрывает все символы внутри большой глобальной структуры: некоторые vтаблицы PerlIO_xxx остаются видимыми. PERL_GLOBAL_STRUCT_PRIVATE затем скрывает все (см., как используется PERLIO_FUNCS_DECL).

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

Первая проблема: определить, какие функции будут функциями публичного API, а какие — частными. Все функции, имена которых начинаются с S_ , являются частными (подумайте «С» для «секретный» или «статический»). Все остальные функции начинаются с «Perl_», но только потому, что функция начинается с «Perl_», не означает, что она является частью API. (См. "Внутренние функции".) Самый простой способ убедиться, что функция является частью API, — найти её запись в perlapi. Если она есть в perlapi, она является частью API. Если её нет, и вы думаете, что она должна быть (т.е., вам нужна для своего расширения), отправьте вопрос на https://github.com/Perl/perl5/issues, объяснив, почему вы считаете, что она должна быть.

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

STATIC void
S_incline(pTHX_ char *s)

STATIC становится «static» в C и может быть #define'd как ничего в некоторых конфигурациях в будущем.

Публичная функция (т.е. часть внутреннего API, но необязательно санкционированная для использования в расширениях) начинается так:

void
Perl_sv_setiv(pTHX_ SV* dsv, IV num)

pTHX_ — это один из ряда макросов (в perl.h), которые скрывают детали контекста интерпретатора. THX обозначает «поток», «этот» или «вещь», в зависимости от случая. (И нет, Джордж Лукас не участвует. :-) Первый символ может быть 'p' для pрототипа, 'a' для aргумента или 'd' для dекларации, поэтому у нас есть pTHX, aTHX и dTHX, и их варианты.

Когда Perl построен без опций, которые устанавливают PERL_IMPLICIT_CONTEXT, нет первого аргумента, содержащего контекст интерпретатора. Конечная нижняя черта в макросе pTHX_ указывает, что расширение макроса требует запятой после аргумента контекста, поскольку за ним следуют другие аргументы. Если PERL_IMPLICIT_CONTEXT не определен, pTHX_ будет проигнорирован, и подпрограмма не будет прототипирована для принятия дополнительного аргумента. Форма макроса без конечной нижней черты используется, когда нет дополнительных явных аргументов.

Когда одна внутренняя функция Perl вызывает другую, она должна передавать контекст. Это обычно скрывается с помощью макросов. Рассмотрим sv_setiv. Он расширяется до чего-то вроде этого:

#ifdef PERL_IMPLICIT_CONTEXT
  #define sv_setiv(a,b)      Perl_sv_setiv(aTHX_ a, b)
  /* can't do this for vararg functions, see below */
#else
  #define sv_setiv           Perl_sv_setiv
#endif

Это работает хорошо, и это означает, что авторы XS могут радостно писать:

sv_setiv(foo, bar);

и при этом это будет работать во всех режимах, с которыми мог быть скомпилирован Perl.

Однако это не работает так чисто для функций varargs, так как макросы предполагают, что количество аргументов известно заранее. Вместо этого нам нужно либо полностью их написать, передав aTHX_ в качестве первого аргумента (ядро Perl, как правило, делает это с функциями, такими как Perl_warner), либо использовать контекстно-независимую версию.

Контекстно-независимая версия Perl_warner называется Perl_warner_nocontext и не принимает дополнительный аргумент. Вместо этого она делает dTHX; для получения контекста из локального хранилища потока. Мы #define warner Perl_warner_nocontext , чтобы расширения получали совместимость исходного кода в ущерб производительности. (Передача аргумента дешевле, чем получение его из локального хранилища потока.)

При просмотре заголовков/источников Perl можно пропустить [pad]THXx. Они предназначены исключительно для использования внутри ядра. Расширениям и подключаемым модулям нужно знать только [pad]THX.

Что случилось с dTHR?

dTHR был представлен в perl 5.005 для поддержки более старой модели потоков. Более старая модель потоков теперь использует механизм THX для передачи указателей на контекст, поэтому dTHR больше не используется. Perl 5.6.0 и более поздние версии по-прежнему поддерживают его для обратной совместимости исходного кода, но он определён как бесполезная операция.

Как использовать всё это в расширениях?

Когда Perl построен с PERL_IMPLICIT_CONTEXT, расширения, которые вызывают любые функции API Perl, должны каким-то образом передать начальный аргумент контекста. Суть в том, что вам нужно написать это так, чтобы расширение все ещё компилировалось, когда Perl не был построен с включённым PERL_IMPLICIT_CONTEXT.

Существует три способа сделать это. Во-первых, простой, но неэффективный способ, который также является по умолчанию, чтобы сохранить совместимость исходного кода с расширениями: всякий раз, когда #включается XSUB.h, он переопределяет макросы aTHX и aTHX_ для вызова функции, которая вернёт контекст. Таким образом, что-то вроде:

sv_setiv(sv, num);

в вашем расширении будет преобразовано в это, когда PERL_IMPLICIT_CONTEXT действует:

Perl_sv_setiv(Perl_get_context(), sv, num);

или в это в противном случае:

Perl_sv_setiv(sv, num);

Вам ничего нового не нужно в вашем расширении, чтобы получить это; поскольку библиотека Perl предоставляет Perl_get_context(), все просто будет работать.

Второй, более эффективный способ — использовать следующий шаблон для вашего Foo.xs:

#define PERL_NO_GET_CONTEXT     /* we want efficiency */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

STATIC void my_private_function(int arg1, int arg2);

STATIC void
my_private_function(int arg1, int arg2)
{
    dTHX;       /* fetch context */
    ... call many Perl API functions ...
}

[... etc ...]

MODULE = Foo            PACKAGE = Foo

/* typical XSUB */

void
my_xsub(arg)
        int arg
    CODE:
        my_private_function(arg, 10);

Обратите внимание, что единственные два изменения по сравнению с обычным способом написания расширения — добавление #define PERL_NO_GET_CONTEXT перед включением заголовков Perl, за которым следует объявление dTHX; в начале каждой функции, которая будет вызывать API Perl. (Вы узнаете, какие функции нуждаются в этом, потому что компилятор C будет жаловаться, что в этих функциях есть неопределённый идентификатор.) Изменения не требуются для самих XSUB, потому что макрос XS() корректно определён, чтобы передать неявный контекст при необходимости.

Третий, ещё более эффективный способ — скопировать то, как это делается внутри ядра Perl:

#define PERL_NO_GET_CONTEXT     /* we want efficiency */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

/* pTHX_ only needed for functions that call Perl API */
STATIC void my_private_function(pTHX_ int arg1, int arg2);

STATIC void
my_private_function(pTHX_ int arg1, int arg2)
{
    /* dTHX; not needed here, because THX is an argument */
    ... call Perl API functions ...
}

[... etc ...]

MODULE = Foo            PACKAGE = Foo

/* typical XSUB */

void
my_xsub(arg)
        int arg
    CODE:
        my_private_function(aTHX_ arg, 10);

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

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

Если Perl компилируется с -DPERL_GLOBAL_STRUCT, то необходимо определение dVAR, если в функции обращаются к глобальным переменным Perl (см. perlvars.h или globvar.sym) и dTHX не используется (dTHX включает dVAR при необходимости). Необходимость в dVAR замечается только при указанном определении времени компиляции, поскольку в противном случае глобальные переменные Perl видны как есть.

Нужно ли делать что-то особенное, если вы вызываете Perl из нескольких потоков?

Если вы создаёте интерпретаторы в одном потоке, а затем вызываете их в другом, вам нужно убедиться, что собственный слот локального хранилища потоков (TLS) Perl правильно инициализирован в каждом из этих потоков.

Функции API perl_alloc и perl_clone автоматически установят слот TLS в интерпретатор, который они создали, так что ничего особенного делать не нужно, если к интерпретатору всегда обращаются в том же потоке, который его создал, и этот поток не создавал или не вызывал других интерпретаторов после этого. Если это не так, вам нужно установить слот TLS потока перед вызовом любых функций API Perl в этом конкретном интерпретаторе. Это делается путём вызова макроса PERL_SET_CONTEXT в этом потоке в первую очередь:

/* do this before doing anything else with some_perl */
PERL_SET_CONTEXT(some_perl);

... other Perl API calls on some_perl go here ...

Будущие планы и PERL_IMPLICIT_SYS

Так же как PERL_IMPLICIT_CONTEXT предоставляет способ объединить всё, что интерпретатор знает о себе, и передать это, так же планируется позволить интерпретатору объединить всё, что он знает об окружающей среде, в которой он работает. Это активируется макросом PERL_IMPLICIT_SYS. В настоящее время он работает только с USE_ITHREADS в Windows.

Это позволяет предоставить дополнительный указатель (называемый «хостовой средой») для всех системных вызовов. Это позволяет всем системным компонентам поддерживать собственное состояние, разбитое на семь структур C. Это тонкие оболочки над обычными системными вызовами (см. win32/perllib.c) для исполняемого файла Perl по умолчанию, но для более амбициозного хоста (такого, который бы эмулировал fork()) вся дополнительная работа, необходимая для имитации того, что разные интерпретаторы на самом деле являются разными «процессами», будет выполнена здесь.

Двигатель/интерпретатор Perl и хост — ортогональные сущности. В процессе может быть один или несколько интерпретаторов и один или несколько «хостов» со свободной ассоциацией между ними.

Внутренние функции

Все внутренние функции Perl, которые будут представлены внешнему миру, имеют префикс Perl_, чтобы они не конфликтовали с функциями XS или функциями, используемыми в программе, в которой Perl встроен. Аналогично, все глобальные переменные начинаются с PL_. (По соглашению, статические функции начинаются с S_.)

Внутри ядра Perl (PERL_CORE определено), вы можете получить доступ к функциям с префиксом Perl_ или без него благодаря множеству определений, которые находятся в embed.h. Обратите внимание, что код расширения не должен устанавливать PERL_CORE; это раскрывает все внутренние компоненты Perl, и, вероятно, вызовет проблемы в XS с каждой новой версией Perl.

Файл embed.h генерируется автоматически из embed.pl и embed.fnc. embed.pl также создаёт файлы заголовков прототипирования для внутренних функций, генерирует документацию и множество других компонентов. Важно, чтобы при добавлении новой функции в ядро или изменении существующей, вы также меняли данные в таблице в embed.fnc. Вот пример записи из этой таблицы:

Apd |SV**   |av_fetch   |AV* ar|I32 key|I32 lval

Первый столбец — набор флагов, второй — тип возвращаемого значения, третий — имя. Столбцы после этого — аргументы. Флаги описаны вверху embed.fnc.

Если вы измените embed.pl или embed.fnc, вам нужно будет запустить make regen_headers , чтобы перестроить embed.h и другие автоматически сгенерированные файлы.

Форматированный вывод IV, UV и NV

Если вы выводите IV, UV или NV вместо форматирования строк stdio(3), таких как %d, %ld, %f, вы должны использовать следующие макросы для обеспечения портативности

IVdf            IV in decimal
UVuf            UV in decimal
UVof            UV in octal
UVxf            UV in hexadecimal
NVef            NV %e-like
NVff            NV %f-like
NVgf            NV %g-like

Эти макросы позаботятся о 64-битных целых числах и длинных двойных числах. Например:

printf("IV is %" IVdf "\n", iv);

Значение IVdf будет расширено до соответствующего формата для IV. Обратите внимание, что пробелы вокруг формата необходимы в случае компиляции кода с C++, для соблюдения стандарта.

Обратите внимание, что существуют различные типы «длинных двойных» (long doubles): Perl будет использовать тот, что задан компилятором.

Если вы печатаете адреса указателей, используйте %p или UVxf в сочетании с PTR2UV().

Форматированный вывод SVs

Содержимое SVs можно вывести с помощью формата SVf, например:

Perl_croak(aTHX_ "This croaked because: %" SVf "\n", SvfARG(err_msg))

где err_msg — это SV.

Не все типы скаляров могут быть выведены. Простые значения определённо могут: IV, UV, NV или PV. Кроме того, если SV является ссылкой на некоторое значение, оно будет разыменовано и значение выведено, или будет отображена информация о типе этого значения и его адресе. Результаты вывода любого другого типа SV не определены и могут привести к аварийному завершению интерпретатора. Значения NV выводятся с использованием формата примерно %g.

Обратите внимание, что пробелы вокруг SVf необходимы в случае компиляции кода с C++, для соблюдения стандарта.

Обратите внимание, что любой файл, выводимый в UTF-8, должен ожидать UTF-8, чтобы получить корректные результаты и избежать предупреждений о широкосимвольных данных. Один из способов сделать это для типичных файлов — вызвать Perl с параметром -C>. (См. «-C [number/list]» в perlrun).

Вы можете использовать это для конкатенации двух скаляров:

SV *var1 = get_sv("var1", GV_ADD);
SV *var2 = get_sv("var2", GV_ADD);
SV *var3 = newSVpvf("var1=%" SVf " and var2=%" SVf,
                    SVfARG(var1), SVfARG(var2));

Форматированный вывод строк

Если вам нужно просто вывести байты в 7-битной строке, завершающейся нулём, вы можете использовать %s (предполагая, что все они действительно только 7-битные). Но если есть вероятность, что значение будет закодировано в UTF-8 или содержит байты выше 0x7F (и, следовательно, 8-битные), используйте вместо этого формат UTF8f. В качестве параметра используйте макрос UTF8fARG().

chr * msg;

/* U+2018: \xE2\x80\x98 LEFT SINGLE QUOTATION MARK
   U+2019: \xE2\x80\x99 RIGHT SINGLE QUOTATION MARK */
if (can_utf8)
  msg = "\xE2\x80\x98Uses fancy quotes\xE2\x80\x99";
else
  msg = "'Uses simple quotes'";

Perl_croak(aTHX_ "The message is: %" UTF8f "\n",
                 UTF8fARG(can_utf8, strlen(msg), msg));

Первый параметр UTF8fARG — логическое значение: 1, если строка в UTF-8; 0, если строка в кодировке по умолчанию (Latin1). Второй параметр — количество байтов строки для вывода. Третий и последний параметр — указатель на первый байт в строке.

Обратите внимание, что любой файл, выводимый в UTF-8, должен ожидать UTF-8, чтобы получить корректные результаты и избежать предупреждений о широкосимвольных данных. Один из способов сделать это для типичных файлов — вызвать Perl с параметром -C>. (См. «-C [number/list]» в perlrun).

Форматированный вывод Size_t и SSize_t

Наиболее общий способ сделать это — привести их к типу UV или IV и вывести, как в предыдущем разделе предыдущем разделе.

Но если вы используете PerlIO_printf(), для уменьшения набора символов и визуального шума используйте модификатор длины %z (для siZe):

PerlIO_printf("STRLEN is %zu\n", len);

Этот модификатор не переносим, поэтому его использование следует ограничить PerlIO_printf().

Указатель на целое число и целое число на указатель

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

PTR2UV(pointer)
PTR2IV(pointer)
PTR2NV(pointer)
INT2PTR(pointertotype, integer)

Например:

IV  iv = ...;
SV *sv = INT2PTR(SV*, iv);

и

AV *av = ...;
UV  uv = PTR2UV(av);

Обработка исключений

Существует несколько макросов для очень базовой обработки исключений в модулях XS. Вы должны определить NO_XSLOCKS перед включением XSUB.h, чтобы использовать эти макросы:

#define NO_XSLOCKS
#include "XSUB.h"

Вы можете использовать эти макросы, если вызываете код, который может сгенерировать ошибку, но вам необходимо выполнить некоторую очистку перед возвратом управления Perl. Например:

dXCPT;    /* set up necessary variables */

XCPT_TRY_START {
  code_that_may_croak();
} XCPT_TRY_END

XCPT_CATCH
{
  /* do cleanup here */
  XCPT_RETHROW;
}

Обратите внимание, что вы всегда должны повторно генерировать исключение, которое было поймано. С помощью этих макросов невозможно просто поймать исключение и проигнорировать его. Если вам нужно проигнорировать исключение, вы должны использовать функцию call_*.

Преимущества использования вышеуказанных макросов заключаются в том, что вам не нужно создавать дополнительную функцию для call_*, и что использование этих макросов быстрее, чем использование call_*.

Документация источника

Ведётся работа по документированию внутренних функций и автоматической генерации справочных руководств из них — perlapi — одно из таких руководств, в котором подробно описываются все функции, доступные авторам XS. perlintern — это сгенерированное автоматически руководство по функциям, которые не являются частью API и, предположительно, предназначены только для внутреннего использования.

Документация источника создаётся путём размещения комментариев POD в исходном коде C, например:

/*
=for apidoc sv_setiv

Copies an integer into the given SV.  Does not handle 'set' magic.  See
L<perlapi/sv_setiv_mg>.

=cut
*/

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

Обратная совместимость

API Perl со временем изменяется. Добавляются новые функции или изменяются интерфейсы существующих функций. Модуль Devel::PPPort пытается предоставить код совместимости для некоторых из этих изменений, чтобы авторы XS не должны были сами кодировать его при поддержке нескольких версий Perl.

Devel::PPPort генерирует заголовочный файл C ppport.h, который также может выполняться как скрипт Perl. Для генерации ppport.h выполните:

perl -MDevel::PPPort -eDevel::PPPort::WriteFile

Помимо проверки существующего кода XS, скрипт также может использоваться для получения информации о совместимости для различных вызовов API с помощью параметра командной строки --api-info. Например:

% perl ppport.h --api-info=sv_magicext

Подробности см. в perldoc ppport.h.

Поддержка Unicode

Perl 5.6.0 ввёл поддержку Unicode. Важно, чтобы портеры и авторы XS понимали эту поддержку и гарантировали, что написанный ими код не повреждает данные Unicode.

Что такое Unicode?

В старые, менее просвещённые времена мы все использовали ASCII. В основном. Главная проблема с ASCII заключается в том, что он американский. Ну, нет, это не проблема; проблема в том, что он не очень полезен для людей, не использующих латинский алфавит. Раньше конкретные языки добавляли свои алфавиты в верхнем диапазоне последовательности, между 128 и 255. Конечно, тогда у нас появилось множество вариантов, которые не были ASCII, и весь смысл стандарта был утерян.

Хуже того, если у вас язык, подобный китайскому или японскому, с сотнями или тысячами символов, то вы действительно не можете поместить их всего в 256, поэтому они должны были полностью отказаться от ASCII и создать свои собственные системы, используя пары чисел для ссылки на один символ.

Для решения этой проблемы некоторые люди основали Unicode, Inc. и создали новый набор символов, содержащий все возможные символы и ещё больше. Существуют разные способы представления этих символов, и тот, который использует Perl, называется UTF-8. UTF-8 использует переменное количество байтов для представления символа. Вы можете узнать больше об Unicode и модели Unicode Perl в perlunicode.

(В платформах EBCDIC Perl вместо этого использует UTF-EBCDIC, который является формой UTF-8, адаптированной для платформ EBCDIC. Ниже мы говорим только о UTF-8. UTF-EBCDIC похож на UTF-8, но детали отличаются. Макросы скрывают различия от вас, просто помните, что конкретные числа и битовые шаблоны, представленные ниже, будут отличаться в UTF-EBCDIC.)

Как определить строку UTF-8?

Нельзя. Потому что данные UTF-8 хранятся в байтах так же, как и данные не-UTF-8. Символ Unicode 200 (0xC8 для вас, кто работает с шестнадцатеричными числами) заглавная буква E с диакритическим знаком grave, представлен двумя байтами v196.172. К сожалению, не-Unicode строка chr(196).chr(172) также содержит эту последовательность байтов. Поэтому вы не можете определить это просто взглянув — это то, что делает вход Unicode интересной проблемой.

В общем случае вам нужно либо знать, с чем вы имеете дело, либо догадываться. Функция API is_utf8_string может помочь; она покажет, содержит ли строка только допустимые символы UTF-8, и вероятность того, что не-UTF-8 строка будет выглядеть как допустимая UTF-8, очень быстро уменьшается с увеличением длины строки. В случае обработки каждого символа isUTF8_CHAR покажет, является ли текущий символ в строке допустимым символом UTF-8.

Как UTF-8 представляет символы Unicode?

Как упоминалось выше, UTF-8 использует переменное количество байтов для хранения символа. Символы со значениями 0...127 хранятся в одном байте, как и хороший старый ASCII. Символ 128 хранится как v194.128; это продолжается до символа 191, который является v194.191. Теперь у нас закончились биты (191 — это двоичное 10111111), поэтому мы переходим дальше; символ 192 — v195.128. И так далее, переходя к трём байтам при символе 2048. «Кодировки Unicode» в perlunicode содержит изображения, как это работает.

Предполагая, что вы знаете, что имеете дело со строкой UTF-8, вы можете узнать, какой длины первый символ в ней с помощью макроса UTF8SKIP:

char *utf = "\305\233\340\240\201";
I32 len;

len = UTF8SKIP(utf); /* len is 2 here */
utf += len;
len = UTF8SKIP(utf); /* len is 3 here */

Другой способ пропустить символы в строке UTF-8 — использовать utf8_hop, который принимает строку и количество символов для пропуска. Проверку границ вы должны осуществлять самостоятельно, поэтому не используйте её легкомысленно.

Все байты в многобайтовом символе UTF-8 будут иметь установленный старший бит, поэтому вы можете проверить, нужно ли делать что-то особенное с этим символом так (UTF8_IS_INVARIANT() — макрос, который проверяет, закодирован ли байт как одиночный байт даже в UTF-8):

U8 *utf;     /* Initialize this to point to the beginning of the
                sequence to convert */
U8 *utf_end; /* Initialize this to 1 beyond the end of the sequence
                pointed to by 'utf' */
UV uv;       /* Returned code point; note: a UV, not a U8, not a
                char */
STRLEN len; /* Returned length of character in bytes */

if (!UTF8_IS_INVARIANT(*utf))
    /* Must treat this as UTF-8 */
    uv = utf8_to_uvchr_buf(utf, utf_end, &len);
else
    /* OK to treat this character as a byte */
    uv = *utf;

Вы также можете увидеть в этом примере, что мы используем utf8_to_uvchr_buf для получения значения символа; обратная функция uvchr_to_utf8 доступна для помещения UV в UTF-8:

if (!UVCHR_IS_INVARIANT(uv))
    /* Must treat this as UTF8 */
    utf8 = uvchr_to_utf8(utf8, uv);
else
    /* OK to treat this character as a byte */
    *utf8++ = uv;

Вы обязательно должны преобразовывать символы в UV с помощью вышеуказанных функций, если вы когда-либо окажетесь в ситуации, когда вам нужно сопоставить символы UTF-8 и не-UTF-8. В этом случае вы не можете пропускать символы UTF-8. Если вы сделаете это, вы потеряете возможность соответствовать символам не-UTF-8 с высоким битом; например, если ваша строка UTF-8 содержит v196.172, и вы пропустите этот символ, вы никогда не сможете сопоставить chr(200) в строке не-UTF-8. Поэтому не делайте этого!

(Обратите внимание, что нам не нужно проверять неизменяемые символы в примерах выше. Функции работают с любым хорошо сформированным входом UTF-8. Просто быстрее избежать накладных расходов функции, когда это не требуется.)

Как Perl хранит строки UTF-8?

В настоящее время Perl обрабатывает строки UTF-8 и не-UTF-8 несколько по-разному. Флаг в SV, SVf_UTF8, указывает, что строка закодирована внутри как UTF-8. Без него значение байта — это число кода символа и наоборот. Этот флаг имеет значение только в том случае, если SV является SvPOK или сразу после строкового представления с помощью SvPV или аналогичного макроса. Вы можете проверить и изменить этот флаг с помощью следующих макросов:

SvUTF8(sv)
SvUTF8_on(sv)
SvUTF8_off(sv)

Этот флаг оказывает важное влияние на обработку строк в Perl: если данные UTF-8 не правильно идентифицированы, регулярные выражения, length, substr и другие операции со строками будут иметь нежелательные (неправильные) результаты.

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

Никогда не забывайте, что флаг SVf_UTF8 отделен от значения PV; необходимо убедиться, что вы случайно не сбрасываете его во время работы с SVs. Более конкретно, вы не можете ожидать сделать следующее:

SV *sv;
SV *nsv;
STRLEN len;
char *p;

p = SvPV(sv, len);
frobnicate(p);
nsv = newSVpvn(p, len);

Строка char* не дает всей картины, и вы не можете скопировать или воссоздать SV, просто скопировав значение строки. Проверьте, установлен ли флаг UTF8 в старом SV (после вызова SvPV) и действуйте соответственно:

p = SvPV(sv, len);
is_utf8 = SvUTF8(sv);
frobnicate(p, is_utf8);
nsv = newSVpvn(p, len);
if (is_utf8)
    SvUTF8_on(nsv);

В приведенном выше примере ваша функция frobnicate была изменена для того, чтобы она учитывала, работает ли она с данными UTF-8, чтобы она могла обработать строку должным образом.

Поскольку простое передача SV в функцию XS и копирование данных SV недостаточно для копирования флагов UTF8, тем более неправильно просто передавать char * в функцию XS.

Для полной общности используйте макрос DO_UTF8, чтобы проверить, должна ли строка в SV обрабатываться как UTF-8. Это учитывает, выполняется ли вызов функции XS из области действия use bytes. Если это так, то основополагающие байты, которые составляют строку UTF-8, должны быть показаны, а не символ, который они представляют. Однако этот пragma следует использовать только для отладки и, возможно, для низкоуровневого тестирования на уровне байтов. Поэтому большинство кодов XS не должны заботиться об этом, но различные области ядра Perl должны его поддерживать.

И это еще не все. Начиная с Perl v5.12, строки, которые не закодированы в UTF-8, также могут обрабатываться как Unicode в различных условиях (см. "ASCII Rules versus Unicode Rules" в perlunicode). Это проблема только для символов, чьи порядковые номера находятся в диапазоне от 128 до 255, и их поведение меняется в зависимости от правил ASCII и Unicode таким образом, что ваш код на это реагирует (см. "The "Unicode Bug"" в perlunicode). Нет опубликованного API для обработки этого, так как оно может меняться, но вы можете посмотреть на код для pp_lc в pp.c в качестве примера того, как это делается в настоящее время.

Как преобразовать строку в UTF-8?

Если вы смешиваете строки UTF-8 и не-UTF-8, необходимо обновить строки, не являющиеся UTF-8, до UTF-8. Если у вас есть SV, самый простой способ сделать это:

sv_utf8_upgrade(sv);

Однако вы не должны делать это, например:

if (!SvUTF8(left))
    sv_utf8_upgrade(left);

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

Вместо этого bytes_to_utf8 даст вам копию своей строковой аргумента, закодированной в UTF-8. Это полезно для того, чтобы данные были доступны для сравнения и т. д., без вреда для исходного SV. Также есть utf8_to_bytes для перехода в обратном направлении, но естественно, это не сработает, если строка содержит какие-либо символы выше 255, которые не могут быть представлены в одном байте.

Как сравнить строки?

"sv_cmp" в perlapi и "sv_cmp_flags" в perlapi выполняют лексикографическое сравнение двух SV и правильно обрабатывают UTF-8. Однако обратите внимание, что Unicode определяет более сложный механизм для сортировки, доступный через модуль Unicode::Collate.

Чтобы просто сравнить две строки на равенство/неравенство, вы можете использовать memEQ() и memNE() как обычно, за исключением того, что строки должны быть закодированы как UTF-8 или не-UTF-8.

Для сравнения двух строк без учета регистра используйте foldEQ_utf8() (строки не обязательно должны иметь одинаковую кодировку UTF-8).

Есть ли еще что-то, что мне нужно знать?

Не особо. Просто запомните эти вещи:

  • Нет способа сказать, является ли строка char * или U8 * UTF-8 или нет. Но вы можете узнать, обрабатывается ли SV как UTF-8, вызвав DO_UTF8 для него после преобразования в строку с помощью SvPV или аналогичного макроса. И вы можете узнать, является ли SV фактически UTF-8 (даже если он не должен обрабатываться как такой), посмотрев на его флаг SvUTF8 (снова после преобразования в строку). Не забудьте установить флаг, если что-то должно быть UTF-8. Считайте флаг частью PV, даже если это не так — если вы передаете PV куда-то, передайте и флаг.

  • Если строка является UTF-8, всегда используйте utf8_to_uvchr_buf для получения значения, если UTF8_IS_INVARIANT(*s), в этом случае вы можете использовать *s.

  • При записи символьного значения UV в строку UTF-8 всегда используйте uvchr_to_utf8, если UVCHR_IS_INVARIANT(uv)), в этом случае вы можете использовать *s = uv.

  • Смешение строк UTF-8 и не-UTF-8 — непростая задача. Используйте bytes_to_utf8 для получения новой строки, закодированной в UTF-8, а затем объедините их.

Пользовательские операторы

Поддержка пользовательских операторов — экспериментальная функция, которая позволяет определять собственные операции. Это в первую очередь для построения интерпретаторов других языков в ядре Perl, но также позволяет оптимизации за счет создания «макро-операций» (операций, выполняющих функции нескольких операций, которые обычно выполняются вместе, таких как gvsv, gvsv, add).

Эта функция реализована как новый тип операции OP_CUSTOM. Ядро Perl ничего не «знает» об этом типе операции и поэтому не будет участвовать ни в каких оптимизациях. Это также означает, что вы можете определять свои пользовательские операции как любую структуру операции — унарную, бинарную, список и так далее — как вам нравится.

Важно знать, чего не смогут сделать пользовательские операторы. Они не позволят вам напрямую добавлять новый синтаксис в Perl. Они даже не позволят вам напрямую добавлять новые ключевые слова. На самом деле, они никак не изменят способ компиляции программы Perl. Вы должны произвести эти изменения самостоятельно после того, как Perl скомпилирует программу. Вы делаете это, либо изменяя дерево операторов с помощью блока CHECK и модуля B::Generate, либо добавив собственный оптимизатор «периодической проверки» с модулем optimize.

При этом вы заменяете обычные операции Perl пользовательскими операциями, создавая операции с типом OP_CUSTOM и функцией PP op_ppaddr своей разработки. Это должно быть определено в XS-коде и должно выглядеть подобно операциям PP в pp_*.c. Вы несете ответственность за обеспечение того, чтобы ваша операция брала нужное количество значений из стека, и вы несете ответственность за добавление меток стека при необходимости.

Вы также должны «зарегистрировать» свою операцию в интерпретаторе Perl, чтобы он мог генерировать осмысленные сообщения об ошибках и предупреждениях. Поскольку возможно иметь несколько пользовательских операций внутри одного «логического» типа операции OP_CUSTOM, Perl использует значение o->op_ppaddr для определения, с какой пользовательской операцией он имеет дело. Вы должны создать структуру XOP для каждого ppaddr, который вы используете, установить свойства пользовательской операции с помощью XopENTRY_set и зарегистрировать структуру по отношению к ppaddr с помощью Perl_custom_op_register. Тривиальный пример может выглядеть так:

static XOP my_xop;
static OP *my_pp(pTHX);

BOOT:
    XopENTRY_set(&my_xop, xop_name, "myxop");
    XopENTRY_set(&my_xop, xop_desc, "Useless custom op");
    Perl_custom_op_register(aTHX_ my_pp, &my_xop);

Доступные поля в структуре:

xop_name

Короткое имя вашей операции. Это будет включено в некоторые сообщения об ошибках, а также будет возвращено как $op->name модулем B, поэтому оно появится в выводе модуля, такого как B::Concise.

xop_desc

Короткое описание функции операции.

xop_class

Какой из различных структур *OP использует эта операция. Это должно быть одно из констант OA_* из op.h, а именно

OA_BASEOP
OA_UNOP
OA_BINOP
OA_LOGOP
OA_LISTOP
OA_PMOP
OA_SVOP
OA_PADOP
OA_PVOP_OR_SVOP

Это следует интерпретировать как 'PVOP' только. _OR_SVOP — потому что единственные ядровые PVOP, OP_TRANS, иногда могут быть SVOP.

OA_LOOP
OA_COP

Другие OA_* константы не должны использоваться.

xop_peep

Этот член типа Perl_cpeep_t, что расшифровывается как void (*Perl_cpeep_t)(aTHX_ OP *o, OP *oldop). Если он установлен, эта функция будет вызвана из Perl_rpeep при обнаружении операций этого типа оптимизатором «периодической проверки». o — это операция, которая требует оптимизации; oldop — это предыдущая оптимизированная операция, чей op_next указывает на o.

B::Generate напрямую поддерживает создание пользовательских операций по имени.

Стек

В описаниях выше иногда упоминается «стек», но на самом деле в интерпретаторе Perl существует множество структур данных, похожих на стеки. Если не указано иное, «стек» обычно относится к стеку значений.

Различные стеки имеют разные цели и работают немного по-разному. Их отличия указаны ниже.

Стек значений

Этот стек хранит значения, с которыми работает обычный код Perl, обычно промежуточные значения выражений в операторе. Сам стек образован массивом указателей на SV.

Основание этого стека указывается переменной интерпретатора PL_stack_base, типа SV **.

Вершина стека — PL_stack_sp, и указывает на последний помещенный элемент.

Элементы помещаются в стек с помощью макроса PUSHs() или его вариантов, описанных выше; XPUSHs(), mPUSHs(), mXPUSHs() и типизированные версии. Внимательно обратите внимание, что не-X версии этих макросов не проверяют размер стека и предполагают, что он достаточно велик. Эти версии должны быть сопряжены с соответствующей проверкой размера стека, такой как макрос EXTEND, чтобы убедиться, что он достаточно велик. Например

EXTEND(SP, 4);
mPUSHi(10);
mPUSHi(20);
mPUSHi(30);
mPUSHi(40);

Это немного производительнее, чем выполнять четыре отдельные проверки в четырех отдельных mXPUSHi() вызовах.

Для дальнейшей оптимизации производительности различные макросы PUSH работают с локальной переменной SP, а не с глобальной переменной интерпретатора PL_stack_sp. Эта переменная объявляется макросом dSP — хотя она обычно подразумевается XSUB и подобными конструкциями, поэтому редко нужно рассматривать её напрямую. После объявления макросы PUSH будут работать только с этой локальной переменной, поэтому перед вызовом других функций ядра Perl необходимо использовать макрос PUTBACK, чтобы вернуть значение из локальной переменной SP обратно в переменную интерпретатора. Аналогично, после вызова функции ядра Perl, которая могла изменить стек или выполнить push/pop операций, необходимо использовать макрос SPAGAIN, который обновляет локальное значение SP из значения интерпретатора.

Элементы из стека извлекаются с помощью макроса POPs или его типизированных версий. Также есть макрос TOPs, который проверяет верхний элемент, не удаляя его.

Обратите особое внимание, что указатели SV на стеке значений не влияют на общий счётчик ссылок на xVs, на которые они ссылаются. Если создаются новые xVs, которые помещаются в стек, необходимо позаботиться об их уничтожении в подходящее время; обычно это делается с помощью одного из макросов mPUSH* или sv_2mortal() для смерти xV.

Стек меток

Стек значений хранит отдельные скалярные значения Perl в качестве временных данных между выражениями. Некоторые выражения Perl работают со списками целиком; для этого нам нужно знать, где в стеке начинается каждый список. Для этого нужен стек меток.

Стек меток хранит целые числа как значения I32, которые представляют собой высоту стека значений в момент перед началом списка; таким образом, сама метка фактически указывает на элемент стека значений, предшествующий списку. Сам список начинается с mark + 1.

База этого стека указывается переменной интерпретатора PL_markstack, типа I32 *.

Вершина стека — PL_markstack_ptr, и указывает на последний добавленный элемент.

Элементы помещаются в стек с помощью макроса PUSHMARK(). Хотя сам стек хранит индексы (стека) значений в виде целых чисел, макросу PUSHMARK следует передавать непосредственно указатель на стек; он рассчитает смещение индекса, сравнив его с переменной PL_stack_sp. Таким образом, код для выполнения этой операции почти всегда выглядит так:

PUSHMARK(SP);

Элементы извлекаются из стека с помощью макроса POPMARK. Также есть макрос TOPMARK, который проверяет верхний элемент, не удаляя его. Эти макросы возвращают целочисленные индексные значения напрямую. Также есть макрос dMARK, который объявляет новую переменную типа SV с двойным указателем, называемую mark, которая указывает на помеченную ячейку стека; этот макрос обычно используется кодом C при работе со списками, представленными в стеке.

Как отмечалось выше, сама переменная mark указывает на последнее добавленное значение в стеке значений перед началом списка, а сам список начинается с mark + 1. Значения списка можно перебирать, используя код, подобный следующему:

for(SV **svp = mark + 1; svp <= PL_stack_sp; svp++) {
  SV *item = *svp;
  ...
}

Обратите особое внимание, что в случае, если список уже пуст, mark будет равно PL_stack_sp.

Поскольку переменная mark преобразуется в указатель на стек значений, необходимо проявлять особую осторожность, если внутри функции вызываются макросы EXTEND или любой из макросов XPUSH, так как стек может потребоваться расширить, и существующий указатель станет недействительным. Если это может быть проблемой, возможным решением является отслеживание смещения метки в виде целого числа и отслеживание самой метки позже, после перемещения стека.

I32 markoff = POPMARK;

...

SP **mark = PL_stack_base + markoff;

Стек временных значений

Как отмечалось выше, ссылки xV в основном стеке значений не влияют на счетчик ссылок xV, и поэтому используется другой механизм для отслеживания моментов, когда временные значения, находящиеся в стеке, должны быть освобождены. Этим занимается стек временных значений.

Стек временных значений хранит указатели на xVs, счётчики ссылок которых будут уменьшены вскоре.

База этого стека указывается переменной интерпретатора PL_tmps_stack, типа SV **.

Вершина стека индексируется PL_tmps_ix, целым числом, которое хранит индекс в массиве последнего добавленного элемента.

Нет публичного API для непосредственного помещения элементов в стек временных значений. Вместо этого используется функция API sv_2mortal() для смерти xV, добавляя его адрес в стек временных значений.

Аналогично, нет публичного API для чтения значений из стека временных значений. Вместо этого используются макросы SAVETMPS и FREETPMS.

Макрос SAVETMPS устанавливает базовые уровни стека временных значений, захватив текущее значение PL_tmps_ix в PL_tmps_floor и сохранив предыдущее значение в стеке сохранения. После этого, каждый раз, когда вызывается FREETMPS, все временные данные, добавленные с момента этого уровня, возвращаются.

Хотя эти два макроса обычно используются парами в паре ENTER/ LEAVE, это не обязательно. Разрешается вызывать FREETMPS несколько раз после последнего вызова SAVETMPS, например, в цикле, итерирующем элементы списка. Хотя можно вызывать SAVETMPS несколько раз в паре областей видимости, это вряд ли будет полезно. Последующие вызовы сместят уровень временных данных вверх, эффективно захватывая существующие временные данные и освобождая их только в конце области видимости.

Стек сохранения

Стек сохранения используется Perl для реализации ключевого слова local и аналогичного поведения; любые операции очистки, которые необходимо выполнить при выходе из текущей области видимости. Элементы, добавленные в этот стек, обычно сохраняют текущее значение некоторой внутренней переменной или состояния, которое будет восстановлено при выходе из области видимости из-за завершения, return, die, goto или по другим причинам.

В то время как другие внутренние стеки Perl хранят отдельные элементы одного и того же типа (обычно указатели SV или целые числа), элементы, добавленные в стек сохранения, имеют различные типы и поля. Например, типу SAVEt_INT необходимо хранить как адрес переменной int для восстановления, так и значение, которое необходимо восстановить. Эту информацию можно было хранить, используя поля структуры struct, но она должна была быть достаточно большой, чтобы хранить три указателя в худшем случае, что привело бы к большому расходу памяти в большинстве случаев с меньшим количеством элементов.

Вместо этого, стек хранит информацию в кодировании переменной длины структур ANY. Конечное значение, добавленное в стек, хранится в поле UV , которое кодирует тип элемента, хранящегося в предыдущих элементах; количество и типы которых зависят от типа хранящегося элемента. Поле типа добавляется последним, так как это первое поле, которое извлекается при восстановлении элементов из стека.

База этого стека указывается переменной интерпретатора PL_savestack, типа ANY *.

Вершина стека индексируется PL_savestack_ix, целым числом, которое хранит индекс в массиве, в который следует поместить следующий элемент. (Обратите внимание, что это отличается от большинства других стеков, которые ссылаются на последний помещённый элемент).

Элементы помещаются в стек сохранения с помощью различных макросов SAVE...(). Многие из этих макросов принимают переменную и сохраняют как её адрес, так и текущее значение в стеке сохранения, обеспечивая восстановление значения при выходе из области видимости.

SAVEI8(i8)
SAVEI16(i16)
SAVEI32(i32)
SAVEINT(i)
...

Также есть ряд других макросов специального назначения, которые сохраняют определённые типы или значения, представляющие интерес. SAVETMPS уже упоминался выше. Другие включают SAVEFREEPV, который организует освобождение PV (т.е. буфера строк), или SAVEDESTRUCTOR, который организует вызов указанной функции при выходе из области видимости. Полный список таких макросов можно найти в scope.h.

Нет публичного API для извлечения отдельных значений или элементов из стека сохранения. Вместо этого, через стек области видимости, пара ENTER и LEAVE служит для начала и завершения вложенных областей видимости. Выход из вложенной области видимости с помощью LEAVE восстановит все сохранённые значения, добавленные с момента последнего вызова ENTER.

Стек области видимости

Как и стек меток для стека значений, стек области видимости образует пару со стеком сохранения. Стек области видимости хранит высоту стека сохранения, в которой начинаются вложенные области видимости, и позволяет восстановить стек сохранения до этой точки при выходе из области видимости.

Когда Perl сконфигурирован с включённой отладкой, эта часть стека также хранит удобочитаемые строковые имена, описывающие тип контекста стека. Каждая операция push сохраняет имя и высоту стека сохранения, а каждая операция pop проверяет верхнее имя с ожидаемым именем, вызывая ошибку утверждения, если имена не совпадают.

База этого стека указывается переменной интерпретатора PL_scopestack, типа I32 *. При включённой отладке имена стека области видимости хранятся в отдельном массиве, на который указывает PL_scopestack_name, типа const char **.

Вершина стека индексируется PL_scopestack_ix, целым числом, которое хранит индекс массива или массивов, в который должен быть добавлен следующий элемент. (Обратите внимание, что это отличается от большинства других стеков, которые ссылаются на последний добавленный элемент).

Значения помещаются в стек области видимости с помощью макроса ENTER, который начинает новую вложенную область видимости. Любые элементы, добавленные в стек сохранения, восстанавливаются при следующем вложенном вызове макроса LEAVE.

Динамическая область видимости и стек контекстов

Примечание: этот раздел описывает непубличный внутренний API, который может быть изменён без предварительного уведомления.

Введение в стек контекстов

В Perl динамическая область видимости относится к временному вложению таких вещей, как вызовы подпрограмм, eval и т.д., а также входу и выходу из блоков области видимости. Например, восстановление local переменной определяется динамической областью видимости.

END_OF_DOCUMENT_MARKER

Perl отслеживает динамический область действия с помощью структуры данных, называемой стеком контекстов, который представляет собой массив структур PERL_CONTEXT, а сам по себе является большой объединением всех типов контекстов. Всякий раз, когда вводится новая область действия (например, блок, for цикл или вызов подпрограммы), новая запись контекста помещается в стек. Аналогично, при выходе из блока или возвращении из вызова подпрограммы и т. д. контекст извлекается. Поскольку стек контекстов представляет собой текущую динамическую область действия, его можно искать. Например, next LABEL ищет в стеке контекст цикла, соответствующий метке; return извлекает контексты до тех пор, пока не найдет контекст подпрограммы или eval или подобный; caller проверяет контексты подпрограмм в стеке.

Каждая запись контекста маркируется типом контекста, cx_type. Типичные типы контекстов — CXt_SUB, CXt_EVAL и т. д., а также CXt_BLOCK и CXt_NULL, которые представляют собой базовую область действия (как помещенную с помощью pp_enter) и сортировочный блок.

Основное разделение в структуре контекста — между областью действия подстановки (CXt_SUBST) и областями действия блоков, которые представляют собой всё остальное. Первая используется только во время выполнения s///e, и далее здесь рассматриваться не будет.

Все типы областей действия блоков имеют общую базу, которая соответствует CXt_BLOCK. Она хранит старые значения различных переменных, связанных с областью действия, таких как PL_curpm, а также информацию о текущей области действия, например, gimme. При выходе из области действия старые переменные восстанавливаются.

Конкретные типы областей действия блоков хранят дополнительную информацию, специфичную для типа. Например, CXt_SUB хранит текущий выполняемый CV, а различные типы циклов for могут содержать исходную переменную цикла SV. При выходе из области действия обрабатываются данные, специфичные для типа; например, счетчик ссылок CV уменьшается, а исходная переменная цикла восстанавливается.

Макрос cxstack возвращает базу текущего стека контекстов, а cxstack_ix — индекс текущей рамки в этом стеке.

Фактически, стек контекстов является частью системы стеков стеков; всякий раз, когда выполняется что-то необычное, например, вызов обработчика DESTROY, или обработчика привязки, новый стек помещается, а затем извлекается в конце.

Обратите внимание, что описанный здесь API существенно изменился в perl 5.24; до этого использовались большие макросы, такие как PUSHBLOCK и POPSUB; в 5.24 они были заменены на описанные ниже встроенные статические функции. Кроме того, порядок и детали работы этих макросов/функций изменились во многих отношениях, часто незаметно. В частности, они не обрабатывали сохранение позиций стеков savestack и temps, и требовали дополнительных ENTER, SAVETMPS и LEAVE по сравнению с новыми функциями. Макросы старого стиля более не будут рассматриваться.

Помещение контекстов

Для помещения нового контекста используются две основные функции: cx = cx_pushblock(), которая помещает новый базовый блок контекста и возвращает его адрес, и семейство подобных функций с именами, такими как cx_pushsub(cx), которые заполняют дополнительные поля, зависящие от типа, в структуре cx. Обратите внимание, что CXt_NULL и CXt_BLOCK не имеют собственных функций помещения, так как они не хранят данных, помимо помещенных с помощью cx_pushblock.

Поля структуры контекста и аргументы функций cx_* могут меняться между выпусками perl, представляя то, что удобно или эффективно для данного выпуска.

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

dMARK;
U8 gimme      = GIMME_V;
bool hasargs  = cBOOL(PL_op->op_flags & OPf_STACKED);
OP *retop     = PL_op->op_next;
I32 old_ss_ix = PL_savestack_ix;
CV *cv        = ....;

/* ... make mortal copies of stack args which are PADTMPs here ... */

/* ... do any additional savestack pushes here ... */

/* Now push a new context entry of type 'CXt_SUB'; initially just
 * doing the actions common to all block types: */

cx = cx_pushblock(CXt_SUB, gimme, MARK, old_ss_ix);

    /* this does (approximately):
        CXINC;              /* cxstack_ix++ (grow if necessary) */
        cx = CX_CUR();      /* and get the address of new frame */
        cx->cx_type        = CXt_SUB;
        cx->blk_gimme      = gimme;
        cx->blk_oldsp      = MARK - PL_stack_base;
        cx->blk_oldsaveix  = old_ss_ix;
        cx->blk_oldcop     = PL_curcop;
        cx->blk_oldmarksp  = PL_markstack_ptr - PL_markstack;
        cx->blk_oldscopesp = PL_scopestack_ix;
        cx->blk_oldpm      = PL_curpm;
        cx->blk_old_tmpsfloor = PL_tmps_floor;

        PL_tmps_floor        = PL_tmps_ix;
    */


/* then update the new context frame with subroutine-specific info,
 * such as the CV about to be executed: */

cx_pushsub(cx, cv, retop, hasargs);

    /* this does (approximately):
        cx->blk_sub.cv          = cv;
        cx->blk_sub.olddepth    = CvDEPTH(cv);
        cx->blk_sub.prevcomppad = PL_comppad;
        cx->cx_type            |= (hasargs) ? CXp_HASARGS : 0;
        cx->blk_sub.retop       = retop;
        SvREFCNT_inc_simple_void_NN(cv);
    */

Обратите внимание, что cx_pushblock() устанавливает два новых уровня: для стека аргументов (до MARK) и стека временных переменных (до PL_tmps_ix). При выполнении на этом уровне области действия каждый nextstate (среди прочих) будет сбрасывать уровни стеков аргументов и временных переменных до этих уровней. Обратите внимание, что так как cx_pushblock использует текущее значение PL_tmps_ix, а не передаёт его как аргумент, это определяет, в какой момент следует вызывать cx_pushblock. В частности, все новые mortal объекты, которые должны быть освобождены только при выходе из области действия (а не на следующем nextstate), должны быть созданы в первую очередь.

Большинство вызывающих функций cx_pushblock просто устанавливают новый уровень стека аргументов на вершину предыдущей рамки стека, но для CXt_LOOP_LIST он хранит элементы, перебираемые в стеке, и поэтому устанавливает blk_oldsp на вершину этих элементов вместо этого. Обратите внимание, что, вопреки своему названию, blk_oldsp не всегда представляет собой значение, которое восстанавливается для PL_stack_sp при выходе из области действия.

Обратите внимание на раннее захват PL_savestack_ix до old_ss_ix, который позже передается как аргумент в cx_pushblock. В случае pp_entersub, это происходит потому, что, хотя большинство значений, требующих сохранения, хранятся в полях структуры контекста, дополнительное значение необходимо сохранять только при работе отладчика, и нет смысла увеличивать размер структуры для этого редкого случая. Поэтому вместо этого оно сохраняется в стеке savestack. Поскольку это значение вычисляется и сохраняется до помещения контекста, необходимо передать старое значение PL_savestack_ix в cx_pushblock, чтобы убедиться, что сохраненное значение освобождается при выходе из области действия. Для большинства пользователей cx_pushblock, где ничего не нужно помещать в стек сохранения, PL_savestack_ix просто передается непосредственно в качестве аргумента в cx_pushblock.

Обратите внимание, что по возможности значения следует сохранять в структуре контекста, а не в стеке сохранения; так намного быстрее.

Обычно cx_pushblock должен сразу следовать за соответствующим cx_pushfoo, без каких-либо элементов между ними; это происходит потому, что если код между ними может завершиться (например, предупреждение повышено до фатального), то код извлечения стека контекстов в dounwind увидит (в примере выше) рамку контекста CXt_SUB, но без всех полей, специфичных для подпрограммы, установленных, и вскоре возникнут сбои.

В случаях, когда два элемента должны быть разделены, первоначально установите тип CXt_NULL или CXt_BLOCK, а затем измените его на CXt_foo при выполнении cx_pushfoo.

Извлечение контекстов

Контексты извлекаются с помощью cx_popsub() и т. д. и cx_popblock(). Однако, в отличие от cx_pushblock, ни одна из этих функций фактически не уменьшает индекс текущего стека контекстов; это делается отдельно с помощью CX_POP().

Существует два основных способа извлечения контекстов. Во время нормального выполнения, когда выходят из областей действия, такие функции, как pp_leave, pp_leaveloop и pp_leavesub, обрабатывают и извлекают только один контекст с помощью cx_popfoo и cx_popblock. С другой стороны, такие вещи, как pp_return и next, могут потребовать извлечения нескольких областей действия до тех пор, пока не будет найден контекст подпрограммы или цикла, а исключения (например, die) требуют извлечения контекстов до тех пор, пока не будет найден контекст eval. Оба эти действия выполняются с помощью dounwind(), которая способна обрабатывать и извлекать все контексты, находящиеся выше целевого.

Вот типичный пример извлечения контекстов, как он встречается в pp_leavesub (несколько упрощен):

U8 gimme;
PERL_CONTEXT *cx;
SV **oldsp;
OP *retop;

cx = CX_CUR();

gimme = cx->blk_gimme;
oldsp = PL_stack_base + cx->blk_oldsp; /* last arg of previous frame */

if (gimme == G_VOID)
    PL_stack_sp = oldsp;
else
    leave_adjust_stacks(oldsp, oldsp, gimme, 0);

CX_LEAVE_SCOPE(cx);
cx_popsub(cx);
cx_popblock(cx);
retop = cx->blk_sub.retop;
CX_POP(cx);

return retop;

Шаги выше выполняются в очень определенном порядке, разработанном как обратный порядок, в котором контекст был помещен. Первое, что нужно сделать, это скопировать и/или защитить любые возвращаемые аргументы и освободить любые временные переменные в текущей области действия. Выходы из области действия, похожие на подпрограмму rvalue, обычно возвращают смертный копию своих возвращаемых аргументов (в отличие от подпрограмм lvalue). Важно сделать эту копию до извлечения записей из стека сохранения или восстановления переменных, иначе могут произойти такие проблемы:

sub f { my $x =...; $x }  # $x freed before we get to copy it
sub f { /(...)/;    $1 }  # PL_curpm restored before $1 copied

Хотя мы хотели бы освободить все временные переменные одновременно, мы должны быть осторожны, чтобы не освободить временные переменные, которые поддерживают возвращаемые аргументы; и не освободить временные переменные, которые мы только что создали, копируя возвращаемые аргументы. К счастью, leave_adjust_stacks() способен делать смертные копии возвращаемых аргументов, перемещать аргументы вниз по стеку и обрабатывать только те записи в стеке временных переменных, которые безопасно сделать.

В контексте void не возвращаются аргументы, поэтому более эффективно пропустить вызов leave_adjust_stacks(). Также в контексте void оператор nextstate, скорее всего, будет вызван в ближайшее время, что приведет к FREETMPS, поэтому нет необходимости делать это.

Следующий шаг — извлечение записей из стека savestack: CX_LEAVE_SCOPE(cx) просто определен как LEAVE_SCOPE(cx->blk_oldsaveix). Обратите внимание, что во время извлечения возможно, что perl вызовет деструкторы, вызовет STORE для отмены локализаций привязанных переменных и т. д. Любой из них может завершиться или вызвать exit(). В этом случае будет вызвано dounwind(), и текущая рамка стека контекстов будет обработана повторно. Таким образом, крайне важно, чтобы все шаги при извлечении контекста выполнялись таким образом, чтобы поддерживать рекурсивный вызов. Другой вариант, уменьшить cxstack_ix перед обработкой рамки, привел бы к утечкам и т. п., если что-то завершилось в середине или перезаписало текущую рамку.

CX_LEAVE_SCOPE сам по себе безопасно рекурсивен: если только половина элементов savestack была извлечена до завершения работы и захвата eval, то CX_LEAVE_SCOPE в dounwind или pp_leaveeval продолжат с того места, где остановился первый.

Следующий шаг — обработка контекста, специфичная для типа; в данном случае cx_popsub. Частично это выглядит так:

cv = cx->blk_sub.cv;
CvDEPTH(cv) = cx->blk_sub.olddepth;
cx->blk_sub.cv = NULL;
SvREFCNT_dec(cv);

где обрабатывается только что выполненный CV. Обратите внимание, что перед уменьшением счетчика ссылок CV, он сбрасывает blk_sub.cv. Это означает, что если он снова войдёт, CV не будет освобождён дважды. Это также означает, что вы не можете полагаться на такие поля, специфичные для типа, содержащие полезные значения после возвращения из cx_popfoo.

Далее, cx_popblock восстанавливает все различные переменные интерпретатора до их предыдущих значений или предыдущих максимальных значений; он расширяется до:

PL_markstack_ptr = PL_markstack + cx->blk_oldmarksp;
PL_scopestack_ix = cx->blk_oldscopesp;
PL_curpm         = cx->blk_oldpm;
PL_curcop        = cx->blk_oldcop;
PL_tmps_floor    = cx->blk_old_tmpsfloor;

Обратите внимание, что он не восстанавливает PL_stack_sp; как упоминалось ранее, какое значение восстановить, зависит от типа контекста (в частности, for (list) {}) и возвращаемых аргументов (если таковые имеются); и это уже было отсортировано ранее leave_adjust_stacks().

Наконец, указатель стека контекста фактически декрементируется на CX_POP(cx). После этого текущая рамка контекста может быть перезаписана другими вставляемыми контекстами. Хотя такие вещи, как связи и DESTROY, должны работать в новом стеке контекста, лучше не предполагать этого. Действительно, в сборках отладки CX_POP(cx) намеренно устанавливает cx в значение null для обнаружения кода, который по-прежнему полагается на значения полей в этой рамке контекста. Обратите внимание в примере pp_leavesub() выше, что мы получаем blk_sub.retop *до* вызова CX_POP.

Переопределение контекстов

Наконец, есть cx_topblock(cx), который действует как супер-nextstate в отношении сброса различных переменных до их базовых значений. Он используется в таких местах, как pp_next, pp_redo и pp_goto, где вместо выхода из области мы хотим перезапустить область. Помимо сброса PL_stack_sp подобно nextstate, он также сбрасывает PL_markstack_ptr, PL_scopestack_ix и PL_curpm. Обратите внимание, что он не выполняет FREETMPS.

Выделение операторов на основе блоков

Примечание: этот раздел описывает непубличный внутренний API, который может быть изменён без предварительного уведомления.

Внутренние механизмы обработки ошибок Perl реализуют die (и его внутренние аналоги) с помощью longjmp. Если это происходит во время лексического анализа, синтаксического анализа или компиляции, мы должны гарантировать, что все операторы, выделенные в рамках процесса компиляции, освобождаются. (Более старые версии Perl не адекватно обрабатывали эту ситуацию: при ошибке парсинга они утекали операторы, которые хранились в C auto переменных и нигде больше не были связаны.)

Для обработки этой ситуации Perl использует блоки операторов, которые прикреплены к текущей компилируемой CV. Блок — это кусок выделенной памяти. Новые операторы выделяются как области блока. Если блок заполняется, создаётся новый (и связывается с предыдущим). При возникновении ошибки и освобождении CV все оставшиеся операторы освобождаются.

Каждый оператор предваряется двумя указателями: один указывает на следующий оператор в блоке, а другой — на блок, которому он принадлежит. Указатель на следующий оператор необходим, чтобы Perl мог итерировать по блоку и освобождать все его операторы. (Структуры операторов имеют различный размер, поэтому операторы блока не могут просто рассматриваться как плотный массив.) Указатель на блок необходим для доступа к счётчику ссылок на блок: когда последний оператор в блоке освобождается, сам блок освобождается.

Выделение операторов в блоке происходит в конце блока. Это позволит выделять листья дерева операторов в первую очередь, и это, возможно, будет более эффективным с точки зрения кэширования. Кроме того, это означает, что нет необходимости хранить размер блока (см. ниже, почему блоки имеют разный размер), поскольку Perl может следовать указателям, чтобы найти последний оператор.

Может показаться возможным полностью исключить счётчики ссылок на блоки, заставив все операторы неявным образом прикрепляться к PL_compcv при выделении и освобождаться при освобождении CV. Это также позволит op_free пропустить FreeOp полностью и, таким образом, освобождать операторы быстрее. Но это не работает в тех случаях, когда операторы должны выживать за пределами своих CV, таких как повторная оценка.

CV также должен иметь счётчик ссылок на блок. Иногда первый созданный оператор сразу освобождается. Если счётчик ссылок на блок достигает 0, то он будет освобождён, причём CV по-прежнему будет указывать на него.

CV использует флаг CVf_SLABBED для указания того, что CV имеет счётчик ссылок на блок. Когда этот флаг установлен, блок доступен через CvSTART, когда CvROOT не установлен, или путём вычитания двух указателей (2*sizeof(I32 *)) из CvROOT при его установке. Альтернативой этому подходу по внедрению блока в CvSTART во время компиляции было бы увеличение структуры xpvcv на ещё один указатель. Но это сделало бы все CV больше, даже несмотря на то, что освобождение операторов на основе блоков обычно выгодно только для программ, которые активно используют строковую оценку.

Когда установлен флаг CVf_SLABBED, CV несёт ответственность за освобождение блока. Если CvROOT не установлен при освобождении или снятии определения CV, предполагается, что произошла ошибка компиляции, поэтому блок операторов обходится, и все операторы освобождаются.

В обычных условиях CV забывает о своём блоке (декрементируя счётчик ссылок) при прикреплении корня. Таким образом, подсчёт ссылок на блок, который происходит при освобождении операторов, позаботится об освобождении блока. В некоторых случаях CV получает указание забыть о блоке (cv_forget_slab) именно для того, чтобы операторы могли выживать после того, как CV будет убран.

Забывание блока при прикреплении корня не строго необходимо, но позволяет избежать потенциальных проблем с переписыванием CvROOT. Везде в ядре и в CPAN есть код, который работает с CvROOT, поэтому забывание блока делает вещи более надёжными и избегает потенциальных проблем.

Поскольку CV берёт на себя ответственность за свой блок при установке флага, этот флаг никогда не копируется при клонировании CV, так как один CV мог освободить блок, на который другой CV всё ещё указывает, так как принудительное освобождение операторов игнорирует счётчик ссылок (но утверждает, что он выглядит правильно).

Чтобы избежать фрагментации блоков, освобождённые операторы помечаются как освобождённые и прикрепляются к цепочке освобождённых операторов блока (идея позаимствована из DBM::Deep). Эти освобождённые операторы повторно используются по возможности. Неиспользование освобождённых операторов было бы проще, но привело бы к значительному увеличению использования памяти для программ с большими if (DEBUG) {...} блоками.

SAVEFREEOP немного проблематично в этой схеме. Иногда это может привести к освобождению оператора после его CV. Если CV принудительно освободил операторы в своём блоке и сам блок, то мы будем работать с освобождённым блоком. Преобразование SAVEFREEOP в бессмысленное действие не помогает, так как иногда оператор может быть сохранённо освобождён, когда нет ошибки компиляции, поэтому оператор никогда не будет освобождён. Он хранит счётчик ссылок на блок, поэтому весь блок будет утекать. Поэтому SAVEFREEOP теперь устанавливает специальный флаг на операторе (->op_savefree). Принудительное освобождение операторов после ошибки компиляции не освободит помеченные таким образом операторы.

Так как многие фрагменты кода создают крошечные подпрограммы, состоящие всего из нескольких операторов, и так как огромный блок будет довольно обременительным для них, первый блок всегда очень небольшой. Чтобы избежать выделения слишком многих блоков для одного CV, каждый последующий блок в два раза больше предыдущего.

Smartmatch ожидает возможности выделения оператора во время выполнения, его запуска и удаления. Для этого оператор просто выделяется с помощью malloced, когда PL_compcv ещё не настроен. Поэтому все операторы, выделенные на основе блоков, помечаются как такие (->op_slabbed), чтобы отличать их от выделенных с помощью malloced.

АВТОРЫ

До мая 1997 года этот документ поддерживал Джефф Окамото <okamoto@corp.hp.com>. Сейчас он поддерживается Perl как часть самого Perl 5 носителями Perl <perl5-porters@perl.org>.

С большой помощью и предложениями от Дина Роэриха, Малкольма Бити, Андреаса Кёнига, Пола Хадсона, Ильи Захаревича, Пола Маркесса, Нила Боуэрса, Мэтью Грина, Тима Банса, Спайдера Бордмана, Ульриха Пфайфера, Стивена МакКэмана и Гурусами Сарати.

См. также

perlapi, perlintern, perlxs, perlembed

© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.32.0/perlguts

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API