Spec-Zone.ru › Perl 5.28

perlguts

СОДЕРЖАНИЕ

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

Также помните, что 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 для размещения заключительного нуля:

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 (смещение верно), сигнализируя другим функциям, что трюк со смещением используется, и смещает указатель PV (называемый SvPVX) вперёд на число удалённых байтов, а также корректирует SvCUR и SvLEN соответственно. (Часть пространства между старым и новым указателями PV используется для хранения количества удалённых байтов.)

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

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

% ./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 выполнит соответствующее преобразование строки в целое число/double или целое число/double в строку.

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

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

Это позволит вам определить, имеете ли вы в своём SV фактически указатель на целое число, double или строку. «p» означает «частный».

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

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

Работа с AV

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

AV*  newAV();

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

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

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

После создания 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 хранит фактические данные в связанном списке структур с типом typedef 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, чтобы избежать утечки памяти.

AV, HV и неопределённые значения

Иногда необходимо хранить неопределённые значения в 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 в HV:

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  Scalar
SVt_PVAV    Array
SVt_PVHV    Hash
SVt_PVCV    Code
SVt_PVGV    Glob (possibly a file handle)

См. "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.

Смертельные ссылки в основном используются для 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 с двойным типом

Переменные скаляров обычно содержат только один тип значения, целое число, double, указатель или ссылку. 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 и более ранних версиях копирование при записи (см. следующий раздел) использовало один и тот же флаг бита со скалярами только для чтения. Поэтому единственный способ проверить, будет ли ошибка «Модификация значения только для чтения» поднята при работе с sv_setsv и т. д., в этих версиях заключается в:

SvREADONLY(sv) && !SvIsCOW(sv)

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

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 не равно NULL, то либо 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_. ПРИМЕЧАНИЕ: магические функции не считаются частью Perl API и могут не экспортироваться библиотекой Perl.

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

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

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 к массивам разрешено, но не оказывает никакого влияния.

Для хэшей есть специальный хук, который даёт возможность контролировать ключи хэша (но не значения). Этот хук вызывает магию 'get' PERL_MAGIC_uvar, если функция "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 — см. "Вызов перловых процедур из 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), чтобы фактически вызвать перловый метод «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() на возвращаемом значении, чтобы фактически вызвать перловый метод «FETCH» для базового объекта TIE. Аналогично, вы можете также вызвать mg_set() на возвращаемом значении после возможной присваивания подходящего значения с помощью sv_setsv, что вызовет метод «STORE» для объекта TIE. [/MAYCHANGE]

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

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

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

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

В Перле есть очень полезная конструкция

{
  local $var = 2;
  ...
}

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

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

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

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

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

В следующем списке API содержатся функции, поэтому необходимо явно предоставить указатели на изменяемые данные (либо указатели C, либо перловые 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 пакета 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.

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

Стек рабочих областей и рекурсия

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

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

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

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

Выделение памяти

Выделение

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

Рекомендуется включить версию malloc, которая распространяется с Perl. Она хранит пулы памяти различных размеров, чтобы быстрее удовлетворять запросы выделения. Однако на некоторых платформах это может вызвать ложные ошибки malloc или free.

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

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, Perl, скомпилированные с экспериментальным определением -DPERL_OP_PARENT, добавляют дополнительный булевый флаг для каждой операции, op_moresib. Когда он не установлен, это указывает, что это последняя операция в цепочке OpSIBLING. Это освобождает поле op_sibling на последнем элементе для указания на родительскую операцию. При этой сборке это поле также переименовано в op_sibparent, чтобы отразить его двойную роль. Макрос OpSIBLING(o) оборачивает это специальное поведение и всегда возвращает NULL для последнего элемента. При этой сборке функция op_parent(o) может использоваться для поиска родителя любой операции. Таким образом, для совместимости с будущими версиями вы всегда должны использовать макрос OpSIBLING(o), а не обращаться к полю op_sibling напрямую.

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

Этап компиляции 1: процедуры проверки

Дерево создается компилятором, пока код yacc поставляет ему конструкции, которые он распознает. Поскольку yacc работает снизу вверх, и первый этап компиляции Perl.

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

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

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

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

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

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

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

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

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

В это время выполняются дополнительные оптимизации, зависящие от контекста. Поскольку на данный момент дерево компиляции содержит обратные ссылки (через указатели «потока»), узлы не могут быть освобождены. Чтобы позволить оптимизированные узлы на этом этапе, такие узлы null()ифицируются вместо освобождения (т.е. их тип изменяется на 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 могут вложены, если в стеке сохранения есть вызов string 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 в дерево op, Perl_dump_packsubs, которая вызывает Perl_dump_sub для всех подпрограмм в пакете, например так: (К счастью, все это xsubs, поэтому нет дерева op)

(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, которая выводит все подпрограммы в хранилище и дерево op основного корня.

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

Обзор и 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.

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

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

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

Вторая проблема: должна быть синтаксическая конструкция, чтобы одни и те же объявления и вызовы подпрограмм могли передавать структуру в качестве первого аргумента или ничего не передавать. Для решения этой проблемы подпрограммы именуются и объявляются определённым образом. Вот типичный фрагмент статической функции, используемой внутри 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), либо использовать контекстно-независимую версию.

END_OF_DOCUMENT_MARKER ```

Бесконтекстная версия 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

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

A

Эта функция является частью публичного API. Все такие функции также должны иметь 'd', очень немногие не имеют.

p

Эта функция имеет префикс Perl_; то есть она определена как Perl_av_fetch.

d

Эта функция имеет документацию с использованием функции apidoc, о которой мы поговорим позже. Некоторые функции имеют 'd', но не 'A'; документация хороша.

Другие доступные флаги:

s

Это статическая функция и она определена как STATIC S_whatever, а обычно вызывается в источниках как whatever(...).

n

Эта функция не нуждается в контексте интерпретатора, поэтому в определении нет pTHX, и отсюда следует, что вызывающие функции не используют aTHX. (См. "Контекст и PERL_IMPLICIT_CONTEXT".)

r

Эта функция никогда не возвращает; croak, exit и аналогичные.

f

Эта функция принимает переменное количество аргументов, стиль printf. Список аргументов должен заканчиваться ..., как в этом примере:

Afprd   |void   |croak          |const char* pat|...
M

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

o

Эта функция не должна иметь макрос совместимости для определения, скажем, Perl_parse в parse. Она должна вызываться как Perl_parse.

x

Эта функция не экспортируется из ядра Perl.

m

Эта функция реализована как макрос.

X

Эта функция явно экспортирована.

E

Эта функция видна для расширений, включённых в ядро Perl.

b

Бинарная обратная совместимость; эта функция является макросом, но также имеет реализацию Perl_ (которая экспортирована).

others

См. комментарии в верхней части 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.

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

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

Форматированный вывод 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 с острым ударением, представлена двумя байтами 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; вам нужно убедиться, что вы случайно не убрали его во время работы с SV. Более конкретно, вы не можете ожидать этого:

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, должны быть открыты, а не символы, которые они представляют. Однако это псевдоним следует использовать только для отладки и, возможно, низкоуровневого тестирования на уровне байтов. Поэтому большинство кода XS не должно беспокоиться об этом, но различные области ядра Perl должны его поддерживать.

И это ещё не вся история. Начиная с Perl v5.12, строки, которые не закодированы в UTF-8, также могут обрабатываться как Unicode при определённых условиях (см. "Правила ASCII против правил Unicode" в perlunicode). Это проблема только для символов, чьи порядковые номера находятся между 128 и 255, и их поведение различается в соответствии с правилами ASCII и Unicode, которые важны для вашего кода (см. "Ошибка Unicode" в 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.

  • При записи кода символа в строку 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 — OP, нуждающийся в оптимизации; oldop — предыдущий оптимизированный OP, чья op_next указывает на o.

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

Динамический диапазон и стек контекстов

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

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

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

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 они были заменены описанными ниже встроенными статическими функциями. Кроме того, порядок и детали работы этих макросов/функций изменились во многих отношениях, часто незаметно. В частности, они не обрабатывали сохранение позиции стека сохранений и стека временных значений и требовали дополнительных 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 (среди прочего) будет сбрасывать уровни стека аргументов и tmps до этих уровней. Обратите внимание, что поскольку cx_pushblock использует текущее значение PL_tmps_ix, а не передаёт его как аргумент, это определяет, в какой момент нужно вызывать cx_pushblock. В частности, все новые временные значения, которые следует освободить только при выходе из области видимости (а не на следующем nextstate), должны быть созданы в первую очередь.

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

Обратите внимание на раннее захват PL_savestack_ix в old_ss_ix, который позже передаётся в качестве аргумента функции cx_pushblock. В случае с pp_entersub это происходит потому, что, хотя большинство значений, требующих сохранения, хранятся в полях структуры контекста, дополнительное значение необходимо сохранить только при запуске отладчика, и нет смысла раздувать структуру для этого редкого случая. Поэтому оно сохраняется в стеке сохранений. Поскольку это значение вычисляется и сохраняется до того, как контекст помещается в стек, необходимо передать старое значение 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. Именно это делает pp_enteriter, как только определяется тип цикла, который он помещает в стек.

Выталкивание контекстов

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

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

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

CX_LEAVE_SCOPE сама по себе безопасно повторно входима: если только половина элементов стека сохранений была извлечена перед завершением и попаданием в ловушку выполнения, то 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.

АВТОРЫ

До мая 1997 года этот документ поддерживался Джеффом Окамото <okamoto@corp.hp.com>. Сейчас он поддерживается в рамках самого Perl командой Perl 5 Porters <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.28.3/perlguts

Spec-Zone.ru

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