perlguts
СОДЕРЖАНИЕ
- ИМЯ
- ОПИСАНИЕ
- Переменные
- Типы данных
- Что такое "IV"?
- Работа с SVs
- Смещения
- Что действительно хранится в SV?
- Работа с AVs
- Работа с HVs
- Расширения API хешей
- AVs, HVs и неопределенные значения
- Ссылки
- Благословенные ссылки и объекты классов
- Создание новых переменных
- Счетчики ссылок и смертность
- Стеклы и глобы
- SV с двойным типом
- Только для чтения значения
- Копирование при записи
- Магические переменные
- Присваивание магии
- Магические виртуальные таблицы
- Поиск магии
- Понимание магии связанных хешей и массивов
- Локализация изменений
- Подпрограммы
- Выделение памяти
- PerlIO
- Компилируемый код
- Просмотр внутренних структур данных с функциями вывода
- Как поддерживаются несколько интерпретаторов и параллельность
- Внутренние функции
- Поддержка Юникода
- Пользовательские операторы
- Динамический объем и стек контекстов
- Выделение операторов на основе блоков
- АВТОРЫ
- СМОТРИТЕ ТАКЖЕ
ИМЯ
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 бит, но на машинах Cray они оба будут 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 строкой. Это удалит различные типы нестроковости из 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 выполнит соответствующее преобразование строки в целое число/число с плавающей точкой или целого числа/числа с плавающей точкой в строку.
Если вам действительно нужно узнать, имеете ли вы указатель на целое число, число с плавающей точкой или строку в SV, вместо этого можно использовать следующие три макроса:
SvIOKp(SV*)
SvNOKp(SV*)
SvPOKp(SV*) Это позволит вам узнать, действительно ли в вашем SV хранится указатель на целое число, число с плавающей точкой или строку. «p» обозначает «приватный».
Существуют различные способы, которыми приватные и публичные флаги могут отличаться. Например, в Perl 5.16 и более ранних версиях привязанный SV может иметь действительное значение в слоте IV (так что SvIOKp истинно), но данные должны извлекаться через функцию FETCH, а не непосредственно, поэтому SvIOK ложно. (В Perl 5.18 и более поздних версиях привязанные скаляры используют флаги таким же образом, как и непривязанные скаляры). Другой случай — когда произошло числовое преобразование и точность была потеряна: флаг только приватный установлен на «потерянных» значениях. Таким образом, когда NV преобразуется в IV с потерей, SvIOKp, SvNOKp и SvNOK будут установлены, но SvIOK не будет.
В общем случае лучше использовать макросы Sv*V.
Работа с AVs
Существует два способа создания и загрузки AV. Первый метод создаёт пустой AV:
AV* newAV(); Второй метод одновременно создаёт AV и первоначально заполняет его SVs:
AV* av_make(SSize_t num, SV **ptr); Второй аргумент указывает на массив, содержащий num SV*. После создания AV SVs можно уничтожить, если это необходимо.
После создания AV возможны следующие операции над ним:
void av_push(AV*, SV*);
SV* av_pop(AV*);
SV* av_shift(AV*);
void av_unshift(AV*, SSize_t num); Это должны быть знакомые операции, за исключением av_unshift. Эта процедура добавляет num элементы в начало массива со значением undef. Затем необходимо использовать av_store (описано ниже), чтобы присвоить значения этим новым элементам.
Вот ещё несколько функций:
SSize_t av_top_index(AV*);
SV** av_fetch(AV*, SSize_t key, I32 lval);
SV** av_store(AV*, SSize_t key, SV* val); Функция av_top_index возвращает наибольшее индексное значение в массиве (как $#array в Perl). Если массив пуст, возвращается -1. Функция av_fetch возвращает значение по индексу key, но если lval не равно нулю, то av_fetch будет хранить неопределённое значение по этому индексу. Функция 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 Array
SVt_PVHV Hash
SVt_PVCV Code
SVt_PVGV Glob (possibly a file handle) Любое числовое значение, возвращённое меньше SVt_PVAV, будет скалярным значением какого-либо типа.
См. "svtype" в perlapi для получения дополнительной информации.
Благословенные ссылки и объекты классов
Ссылки также используются для поддержки объектно-ориентированного программирования. В лексиконе ОО Perl объект — это просто ссылка, которая была благословлена в пакет (или класс). После благословения программист может использовать ссылку для доступа к различным методам в классе.
Ссылка может быть благословлена в пакет с помощью следующей функции:
SV* sv_bless(SV* sv, HV* stash); Аргумент sv должен быть значением ссылки. Аргумент stash определяет, к какому классу будет принадлежать ссылка. См. "Stashes and Globs" для получения информации о преобразовании имён классов в стеки.
/* Всё ещё в разработке */
Следующая функция повышает rv до ссылки, если это не ссылка. Создаёт новый SV для ссылки rv. Если classname не равно NULL, SV благословляется в указанный класс. Возвращается SV.
SV* newSVrv(SV* rv, const char* classname); END_OF_DOCUMENT_MARKER Следующие три функции копируют целое число, целое без знака или двойное число в SV, ссылка на который находится в rv. SV благословлён, если classname не равен нулю.
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 не равен нулю.
SV* sv_setref_pv(SV* rv, const char* classname, void* pv); Следующая функция копирует строку в SV, ссылка на который находится в rv. Установите длину в 0, чтобы позволить Perl вычислить длину строки. SV благословлён, если classname не равен нулю.
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 с двойным типом
Скалярные переменные обычно содержат только один тип значения: целое число, двойное число, указатель или ссылку. 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 и т. д. к ошибке "Modification of a read-only value" в этих версиях, это:
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 не равно нулю, то либо копия 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 как значение из 5 элементов. В настоящее время эти три элемента используются исключительно кодом потоков и могут сильно измениться.
Текущие типы магических виртуальных таблиц:
mg_type
(old-style char and macro) MGVTBL Type of magic
-------------------------- ------ -------------
\0 PERL_MAGIC_sv vtbl_sv Special scalar variable
# PERL_MAGIC_arylen vtbl_arylen Array length ($#ary)
% PERL_MAGIC_rhash (none) Extra data for restricted
hashes
* PERL_MAGIC_debugvar vtbl_debugvar $DB::single, signal, trace
vars
. PERL_MAGIC_pos vtbl_pos pos() lvalue
: PERL_MAGIC_symtab (none) Extra data for symbol
tables
< PERL_MAGIC_backref vtbl_backref For weak ref data
@ PERL_MAGIC_arylen_p (none) To move arylen out of XPVAV
B PERL_MAGIC_bm vtbl_regexp Boyer-Moore
(fast string search)
c PERL_MAGIC_overload_table vtbl_ovrld Holds overload table
(AMT) on stash
D PERL_MAGIC_regdata vtbl_regdata Regex match position data
(@+ and @- vars)
d PERL_MAGIC_regdatum vtbl_regdatum Regex match position data
element
E PERL_MAGIC_env vtbl_env %ENV hash
e PERL_MAGIC_envelem vtbl_envelem %ENV hash element
f PERL_MAGIC_fm vtbl_regexp Formline
('compiled' format)
g PERL_MAGIC_regex_global vtbl_mglob m//g target
H PERL_MAGIC_hints vtbl_hints %^H hash
h PERL_MAGIC_hintselem vtbl_hintselem %^H hash element
I PERL_MAGIC_isa vtbl_isa @ISA array
i PERL_MAGIC_isaelem vtbl_isaelem @ISA array element
k PERL_MAGIC_nkeys vtbl_nkeys scalar(keys()) lvalue
L PERL_MAGIC_dbfile (none) Debugger %_<filename
l PERL_MAGIC_dbline vtbl_dbline Debugger %_<filename
element
N PERL_MAGIC_shared (none) Shared between threads
n PERL_MAGIC_shared_scalar (none) Shared between threads
o PERL_MAGIC_collxfrm vtbl_collxfrm Locale transformation
P PERL_MAGIC_tied vtbl_pack Tied array or hash
p PERL_MAGIC_tiedelem vtbl_packelem Tied array or hash element
q PERL_MAGIC_tiedscalar vtbl_packelem Tied scalar or handle
r PERL_MAGIC_qr vtbl_regexp Precompiled qr// regex
S PERL_MAGIC_sig (none) %SIG hash
s PERL_MAGIC_sigelem vtbl_sigelem %SIG hash element
t PERL_MAGIC_taint vtbl_taint Taintedness
U PERL_MAGIC_uvar vtbl_uvar Available for use by
extensions
u PERL_MAGIC_uvar_elem (none) Reserved for use by
extensions
V PERL_MAGIC_vstring (none) SV was vstring literal
v PERL_MAGIC_vec vtbl_vec vec() lvalue
w PERL_MAGIC_utf8 vtbl_utf8 Cached UTF-8 information
x PERL_MAGIC_substr vtbl_substr substr() lvalue
Y PERL_MAGIC_nonelem vtbl_nonelem Array element that does not
exist
y PERL_MAGIC_defelem vtbl_defelem Shadow "foreach" iterator
variable / smart parameter
vivification
\ PERL_MAGIC_lvref vtbl_lvref Lvalue reference
constructor
] PERL_MAGIC_checkcall vtbl_checkcall Inlining/mutation of call
to this CV
~ PERL_MAGIC_ext (none) Available for use by
extensions Когда в таблице присутствуют как прописные, так и строчные буквы, то прописная буква обычно используется для представления некоторого составного типа (списка или хэша), а строчная буква используется для представления элемента этого составного типа. Некоторые внутренние коды используют эту связь между заглавными и строчными буквами. Однако 'v' и 'V' (vec и v-строка) никак не связаны.
Магические типы PERL_MAGIC_ext и PERL_MAGIC_uvar определены специально для использования расширениями и не будут использоваться самим Perl. Расширения могут использовать магию PERL_MAGIC_ext для прикрепления приватной информации к переменным (как правило, к объектам). Это особенно полезно, так как обычный код Perl не может повредить эту приватную информацию (в отличие от использования дополнительных элементов объекта хэша).
Аналогично, магия PERL_MAGIC_uvar может использоваться так же, как tie(), для вызова C-функции всякий раз, когда используется или изменяется значение скалярной переменной. Поле MAGIC's mg_ptr указывает на структуру ufuncs:
struct ufuncs {
I32 (*uf_val)(pTHX_ IV, SV*);
I32 (*uf_set)(pTHX_ IV, SV*);
IV uf_index;
}; Когда SV считывается или записывается, вызывается функция uf_val или uf_set с uf_index в качестве первого аргумента и указателем на SV во втором. Простой пример добавления магии PERL_MAGIC_uvar показан ниже. Обратите внимание, что структура ufuncs копируется функцией sv_magic, поэтому вы можете безопасно выделить её в стеке.
void
Umagic(sv)
SV *sv;
PREINIT:
struct ufuncs uf;
CODE:
uf.uf_val = &my_get_fn;
uf.uf_set = &my_set_fn;
uf.uf_index = 0;
sv_magic(sv, 0, PERL_MAGIC_uvar, (char*)&uf, sizeof(uf)); Прикрепление PERL_MAGIC_uvar к массивам разрешается, но не имеет эффекта.
Для хэшей существует специализированный крючок, который даёт контроль над ключами хэша (но не над значениями). Этот крючок вызывает магию PERL_MAGIC_uvar 'get', если функция "set" в структуре ufuncs равна NULL. Крючок активируется всякий раз, когда хэш доступен с ключом, указанным как SV через функции hv_store_ent, hv_fetch_ent, hv_delete_ent и hv_exists_ent. Доступ к ключу как строке через функции без суффикса ..._ent обходит крючок. См. "GUTS" в Hash::Util::FieldHash для подробного описания.
Поскольку несколько расширений могут использовать магию PERL_MAGIC_ext или PERL_MAGIC_uvar, важно, чтобы расширения принимали особые меры предосторожности, чтобы избежать конфликтов. Обычно достаточно использовать магию только на объектах, благословлённых в том же классе, что и расширение. Для магии PERL_MAGIC_ext рекомендуется определять MGVTBL, даже если все его поля будут 0, чтобы отдельные указатели MAGIC можно было идентифицировать как определённый вид магии по их магической виртуальной таблице. mg_findext предлагает простой способ сделать это:
STATIC MGVTBL my_vtbl = { 0, 0, 0, 0, 0, 0, 0, 0 };
MAGIC *mg;
if ((mg = mg_findext(sv, PERL_MAGIC_ext, &my_vtbl))) {
/* this is really ours, not another module's PERL_MAGIC_ext */
my_priv_data_t *priv = (my_priv_data_t *)mg->mg_ptr;
...
} Также обратите внимание, что функции sv_set*() и sv_cat*(), описанные ранее, не вызывают магический метод 'set' для своих целевых объектов. Это необходимо сделать пользователю, либо вызвав макрос SvSETMAGIC() после этих функций, либо используя одну из функций sv_set*_mg() или sv_cat*_mg(). Аналогично, код на языке C должен вызывать макрос SvGETMAGIC(), чтобы вызвать магический метод 'get', если он использует SV, полученный из внешних источников, в функциях, которые не обрабатывают магию. См. perlapi для описания этих функций. Например, вызовы функций sv_cat*() обычно должны следовать за вызовом функции SvSETMAGIC(), но не требуют предварительного вызова SvGETMAGIC(), так как их реализация обрабатывает магический метод 'get'.
Поиск магии
MAGIC *mg_find(SV *sv, int type); /* Finds the magic pointer of that
* type */ Эта процедура возвращает указатель на структуру MAGIC, хранящуюся в SV. Если SV не содержит этой магической особенности, возвращается NULL. Если SV содержит несколько экземпляров этой магической особенности, будет возвращен первый из них. mg_findext можно использовать для поиска структуры MAGIC SV на основе как типа магии, так и виртуальной таблицы магии:
MAGIC *mg_findext(SV *sv, int type, MGVTBL *vtbl); Кроме того, если SV, переданный в mg_find или mg_findext, не имеет тип SVt_PVMG, Perl может завершиться с ошибкой.
int mg_copy(SV* sv, SV* nsv, const char* key, STRLEN klen); Эта процедура проверяет, какие типы магии имеет sv. Если поле mg_type является заглавной буквой, то mg_obj копируется в nsv, но поле mg_type изменяется на строчную букву.
Понимание магии привязанных хэшей и массивов
Привязанные хэши и массивы представляют собой магических существ типа магии PERL_MAGIC_tied.
ПРЕДУПРЕЖДЕНИЕ: Начиная с релиза 5.004, для правильного использования функций доступа к массивам и хэшам требуется понимание нескольких нюансов. Некоторые из этих нюансов фактически считаются ошибками в API, которые будут исправлены в более поздних версиях, и выделены ниже [MAYCHANGE]. Если вы обнаруживаете, что применяете такую информацию в этом разделе, имейте в виду, что поведение может измениться в будущем, мм, без предупреждения.
Функция perl tie связывает переменную с объектом, который реализует различные методы GET, SET и т. д. Для выполнения эквивалента функции perl tie из XSUB, необходимо имитировать это поведение. Приведенный ниже код выполняет необходимые шаги — во-первых, создает новый хэш, а затем создает второй хэш, который благословляет в класс, который будет реализовывать методы tie. Наконец, связывает два хэша вместе и возвращает ссылку на новый привязанный хэш. Обратите внимание, что приведенный ниже код НЕ вызывает метод TIEHASH в классе MyTie — см. "Вызов Perl-процедур из C-программ" для получения подробной информации о том, как это сделать.
SV*
mytie()
PREINIT:
HV *hash;
HV *stash;
SV *tie;
CODE:
hash = newHV();
tie = newRV_noinc((SV*)newHV());
stash = gv_stashpv("MyTie", GV_ADD);
sv_bless(tie, stash);
hv_magic(hash, (GV*)tie, PERL_MAGIC_tied);
RETVAL = newRV_noinc(hash);
OUTPUT:
RETVAL Функция av_store, когда ей передается привязанный массив в качестве аргумента, просто копирует магию массива на значение, которое будет "сохранено", используя mg_copy. Она также может вернуть NULL, что указывает на то, что значение фактически не нужно было сохранять в массиве. [MAYCHANGE] После вызова av_store на привязанном массиве вызывающий обычно должен вызвать mg_set(val), чтобы фактически вызвать perl-уровень метода "STORE" на объекте TIEARRAY. Если av_store вернула NULL, вызов SvREFCNT_dec(val) также обычно необходим для предотвращения утечки памяти. [/MAYCHANGE]
Предыдущий абзац полностью применим к доступу к привязанным хэшам с использованием функций hv_store и hv_store_ent.
av_fetch и соответствующие функции для хэшей hv_fetch и hv_fetch_ent фактически возвращают неопределенное смертное значение, чья магия была инициализирована с помощью mg_copy. Обратите внимание, что возвращаемое значение не нужно освобождать, так как оно уже смертное. [MAYCHANGE] Но вам необходимо вызвать mg_get() на возвращаемом значении, чтобы фактически вызвать perl-уровень метода "FETCH" на базовом объекте TIE. Аналогично, можно вызвать mg_set() на возвращаемом значении после возможной присвоения подходящего значения с использованием sv_setsv, что вызовет метод "STORE" на объекте TIE. [/MAYCHANGE]
[MAYCHANGE] Другими словами, функции получения/сохранения массивов или хэшей на самом деле не получают и не сохраняют фактические значения в случае привязанных массивов и хэшей. Они просто вызывают mg_copy, чтобы прикрепить магию к значениям, которые должны были быть "сохранены" или "получены". Более поздние вызовы mg_get и mg_set фактически выполняют работу по вызову методов TIE на базовых объектах. Таким образом, механизм магии в настоящее время реализует своего рода леничный доступ к массивам и хэшам.
В настоящее время (начиная с версии Perl 5.004), использование функций доступа к хэшам и массивам требует, чтобы пользователь понимал, работают ли они с "обычными" хэшами и массивами или с их привязанными вариантами. В будущих версиях API может быть изменен для обеспечения более прозрачного доступа к привязанным и обычным типам данных. [/MAYCHANGE]
Вам будет полезно понимать, что интерфейсы TIEARRAY и TIEHASH представляют собой всего лишь синтаксический сахар, чтобы вызвать некоторые perl-методы, используя унифицированный синтаксис хэшей и массивов. Использование этого сахара вносит некоторую издержки (как правило, около двух-четырех дополнительных инструкций opcode на операцию FETCH/STORE, помимо создания всех требуемых смертных переменных для вызова методов). Эти издержки будут сравнительно невелики, если методы TIE сами по себе значительны, но если они состоят только из нескольких инструкций, эти издержки не будут незначительны.
Локализация изменений
В Perl есть очень удобная конструкция
{
local $var = 2;
...
} Эта конструкция приблизительно эквивалентна
{
my $oldvar = $var;
$var = 2;
...
$var = $oldvar;
} Главное отличие заключается в том, что первая конструкция восстановит начальное значение $var, независимо от того, как управление выходит из блока: goto, return, die/eval и т. д. Она также немного эффективнее.
Существует способ достижения аналогичной задачи из C через Perl API: создать псевдоблок и организовать автоматическое отмену некоторых изменений в конце блока, явным образом или через нелокальный выход (через die()). Конструкция типа блок создается с помощью пары макросов ENTER/LEAVE (см. "Возвращение скаляра" в perlcall). Такая конструкция может быть создана специально для какой-либо важной задачи локального изменения или может использоваться существующая (например, границы охватывающей Perl-подпрограммы/блока или существующей пары для освобождения TMP). (Во втором случае издержки дополнительной локализации должны быть почти незначительными). Обратите внимание, что любой XSUB автоматически заключен в пару ENTER/LEAVE.
Внутри такого псевдоблока доступна следующая служба:
-
SAVEINT(int i) -
SAVEIV(IV i) -
SAVEI32(I32 i) -
SAVELONG(long i) -
Эти макросы организуют восстановление значения целочисленной переменной
iв конце окружающего псевдоблока. -
SAVESPTR(s) -
SAVEPPTR(p) -
Эти макросы организуют восстановление значений указателей
sиp.sдолжен быть указателем типа, который выдерживает преобразование вSV*и обратно,pдолжен быть способен выдержать преобразование вchar*и обратно. -
SAVEFREESV(SV *sv) -
Счетчик ссылок
svбудет уменьшен в конце псевдоблока. Это похоже наsv_2mortal, поскольку это также механизм для выполнения отложенногоSvREFCNT_dec. Однако, в то время какsv_2mortalпродлевает срок жизниsvдо начала следующей инструкции,SAVEFREESVпродлевает его до конца охватывающего области видимости. Эти сроки жизни могут сильно отличаться.Также сравните
SAVEMORTALIZESV. -
SAVEMORTALIZESV(SV *sv) -
Точно так же, как
SAVEFREESV, но смертельнаsvв конце текущего области видимости вместо уменьшения его счетчика ссылок. Это обычно приводит к тому, чтоsvостается активным до тех пор, пока инструкция, вызвавшая текущую активную область видимости, не завершит выполнение. -
SAVEFREEOP(OP *op) -
OP *освобождается с помощью op_free() в конце псевдоблока. -
SAVEFREEPV(p) -
Блок памяти, на который указывает
p, освобождается с помощью Safefree() в конце псевдоблока. -
SAVECLEARSV(SV *sv) -
Очищает ячейку в текущем наборе "рабочих данных", соответствующую
sv, в конце псевдоблока. -
SAVEDELETE(HV *hv, char *key, I32 length) -
Ключ
keyхэшаhvудаляется в конце псевдоблока. Строка, на которую указываетkey, освобождается с помощью Safefree(). Если у вас есть ключ в хранилище с коротким сроком жизни, соответствующая строка может быть перевыделена так:SAVEDELETE(PL_defstash, savepv(tmpbuf), strlen(tmpbuf)); -
SAVEDESTRUCTOR(DESTRUCTORFUNC_NOCONTEXT_t f, void *p) -
В конце псевдоблока вызывается функция
fс единственным аргументомp. -
SAVEDESTRUCTOR_X(DESTRUCTORFUNC_t f, void *p) -
В конце псевдоблока вызывается функция
fс неявным аргументом контекста (если он есть) иp. -
SAVESTACK_POS() -
Текущий смещение на внутреннем стеке Perl (см.
SP) восстанавливается в конце псевдоблока.
Следующий список API содержит функции, поэтому необходимо явно указать указатели на изменяемые данные (либо указатели C, либо перл-подобные GV *). Там, где вышеуказанные макросы принимают int, аналогичная функция принимает int *.
-
SV* save_scalar(GV *gv) -
Эквивалентно коду Perl
local $gv. -
AV* save_ary(GV *gv) -
HV* save_hash(GV *gv) -
Аналогично
save_scalar, но локализует@gvи%gv. -
void save_item(SV *item) -
Дублирует текущее значение
SV; при выходе из текущегоENTER/LEAVEпсевдоблока восстановит значениеSVс помощью сохранённого значения. Не обрабатывает магию. Используйтеsave_scalar, если магия затронута. -
void save_list(SV **sarg, I32 maxsarg) -
Вариант
save_item, принимающий несколько аргументов через массивsargизSV*элементов длинойmaxsarg. -
SV* save_svref(SV **sptr) -
Аналогично
save_scalar, но восстановитSV *. -
void save_aptr(AV **aptr) -
void save_hptr(HV **hptr) -
Аналогично
save_svref, но локализуетAV *иHV *.
Модуль Alias реализует локализацию основных типов в области видимости вызывающей функции. Людей, интересующихся локализации в области видимости содержащей функции, также следует изучить его.
Подпрограммы
XSUB и стек аргументов
Механизм XSUB — простой способ для программ Perl доступа к C-подпрограммам. Подпрограмма XSUB имеет стек, содержащий аргументы из программы Perl, и способ сопоставления данных Perl с C-эквивалентами.
Аргументы стека доступны через макрос ST(n), который возвращает n-й аргумент стека. Аргумент 0 — первый переданный аргумент в вызове Perl-подпрограммы. Эти аргументы являются SV* и могут быть использованы везде, где используется SV*.
В большинстве случаев результат работы C-функции можно обработать с помощью директив RETVAL и OUTPUT. Однако существуют случаи, когда стек аргументов недостаточно велик для обработки всех возвращаемых значений. Пример — вызов POSIX tzname(), который не принимает аргументов, но возвращает два — стандартное и летнее время зоны.
Для обработки этой ситуации используется директива PPCODE, и стек расширяется с помощью макроса:
EXTEND(SP, num); где SP — макрос, представляющий локальную копию указателя стека, и num — количество элементов, на которое необходимо расширить стек.
Теперь, когда в стеке есть место, значения можно поместить в него с помощью макроса PUSHs. Помещаемые значения часто должны быть «временными» (см. "Счётчики ссылок и временные объекты"):
PUSHs(sv_2mortal(newSViv(an_integer)))
PUSHs(sv_2mortal(newSVuv(an_unsigned_integer)))
PUSHs(sv_2mortal(newSVnv(a_double)))
PUSHs(sv_2mortal(newSVpv("Some String",0)))
/* Although the last example is better written as the more
* efficient: */
PUSHs(newSVpvs_flags("Some String", SVs_TEMP)) И теперь, когда программа Perl вызывает tzname, два значения будут присвоены как:
($standard_abbrev, $summer_abbrev) = POSIX::tzname; Альтернативный (и, возможно, более простой) метод помещения значений в стек — использование макроса:
XPUSHs(SV*) Этот макрос автоматически корректирует стек по мере необходимости. Таким образом, вам не нужно вызывать EXTEND для расширения стека.
Несмотря на рекомендации в предыдущих версиях этого документа, макросы (X)PUSH[iunp] не подходят для XSUB, возвращающих несколько результатов. Для этого либо придерживайтесь макросов (X)PUSHs, показанных выше, либо используйте новые макросы m(X)PUSH[iunp]; см. "Помещение C-значения в Perl-стек".
Для получения дополнительной информации, см. perlxs и perlxstut.
Автозагрузка с XSUB
Если функция AUTOLOAD является XSUB, как и Perl-функции, Perl помещает полное имя загружаемой функции AUTOLOAD в переменную $AUTOLOAD пакета XSUB.
Но он также помещает ту же информацию в определённые поля самого XSUB:
HV *stash = CvSTASH(cv);
const char *subname = SvPVX(cv);
STRLEN name_length = SvCUR(cv); /* in bytes */
U32 is_utf8 = SvUTF8(cv); SvPVX(cv) содержит только само имя подпрограммы, без пакета. Для функции AUTOLOAD в UNIVERSAL или одном из его суперклассов CvSTASH(cv) возвращает NULL при вызове метода несуществующего пакета.
Примечание: Установка $AUTOLOAD перестала работать в 5.6.1, так как он не поддерживал XSUB-функции 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, которые являются целями для кодов операций. Ответ заключается в том, что они создаются при компиляции текущей единицы — подпрограммы или файла (для кодов операций для операторов вне подпрограмм).
Скретчпад хранит SV, которые являются лексическими переменными для текущей единицы и являются целями для кодов операций. Предыдущая версия этого документа утверждала, что можно определить, что SV находится в скретчпаде, посмотрев на его флаги: лексические переменные имеют SVs_PADMY установлен, а цели имеют SVs_PADTMP установлен. Но это никогда не было полностью верно. SVs_PADMY может быть установлен на переменную, которая больше не находится в любом скретчпаде. Хотя цели действительно имеют SVs_PADTMP установлен, он также может быть установлен на переменные, которые никогда не находились в скретчпаде, но тем не менее действуют как цели. Начиная с perl 5.21.5, флаг SVs_PADMY больше не используется и определён как 0. SvPADMY() теперь возвращает true для всего, что не имеет SVs_PADTMP.
Соответствие между операциями OP и целями не является однозначным. Разные OP в дереве компиляции единицы могут использовать одну и ту же цель, если это не противоречит ожидаемому времени жизни временной переменной.
Скретчпады и рекурсия
На самом деле не совсем верно, что скомпилированная единица содержит указатель на скретчпад AV. На самом деле она содержит указатель на AV (изначально) с одним элементом, и этот элемент — скретчпад AV. Зачем нам нужен дополнительный уровень косвенности?
Ответ — рекурсия и, возможно, потоки. Оба эти варианта могут создать несколько указателей выполнения, направленных в одну и ту же подпрограмму. Чтобы подпрограмма-дочерняя не перезаписывала временные переменные для подпрограммы-родительской (жизнь которой охватывает вызов дочерней), родительская и дочерняя подпрограммы должны иметь разные скретчпады (и лексические переменные должны быть отдельными!).
Таким образом, каждая подпрограмма рождается с массивом скретчпадов (длиной 1). При каждом входе в подпрограмму проверяется, что текущая глубина рекурсии не превышает длину этого массива. Если превышает, создаётся новый скретчпад и помещается в массив.
Цели в этом скретчпаде являются undef, но они уже помечены правильными флагами.
Выделение памяти
Выделение
Вся память, предназначенная для использования с функциями 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, и они соединяются различными способами.
Простейший тип структуры op — это OP: у него нет дочерних узлов. Унарные операторы, UNOP, имеют один дочерний узел, и он указан в поле op_first. Бинарные операторы (BINOP) имеют не только поле op_first, но и поле op_last. Самый сложный тип op — это LISTOP, у которого может быть любое количество дочерних узлов. В этом случае первый дочерний узел указан в поле op_first, а последний — в поле op_last. Дочерние узлы между ними можно найти, итеративно следуя указателю OpSIBLING от первого дочернего узла до последнего (но см. ниже).
Есть и другие типы op: PMOP хранит регулярное выражение и не имеет дочерних узлов, а LOOP может или не может иметь дочерние узлы. Если поле op_children не равно нулю, оно ведёт себя как LISTOP. Для усложнения, если UNOP на самом деле является оператором null после оптимизации (см. "Этап компиляции 2: распространение контекста"), он всё ещё будет иметь дочерние узлы в соответствии со своим прежним типом.
Наконец, есть LOGOP или логическая операция. Как и LISTOP, она имеет один или несколько дочерних узлов, но не имеет поля op_last: поэтому вы должны следовать за op_first, а затем за цепочкой OpSIBLING, чтобы найти последний дочерний узел. Вместо этого у неё есть поле op_other, которое сопоставимо с полем op_next, описанным ниже, и представляет собой альтернативный путь выполнения. Операторы, такие как and, or и ?, являются LOGOP. Обратите внимание, что в общем случае op_other может не указывать ни на один из непосредственных дочерних узлов LOGOP.
Начиная с версии 5.21.2, в перлах, построенных с экспериментальным определением -DPERL_OP_PARENT, добавлен дополнительный булевый флаг для каждой операции, op_moresib. Если он не установлен, это означает, что это последняя операция в цепочке OpSIBLING. Это освобождает поле op_sibling в последнем элементе списка для указания на родительскую операцию. При этом построении это поле также переименовывается в op_sibparent, чтобы отразить его совместную роль. Макрос OpSIBLING(o) обрабатывает это специальное поведение и всегда возвращает NULL для последнего элемента списка. С этим построением можно использовать функцию op_parent(o), чтобы найти родителя любой операции. Таким образом, для обеспечения обратной совместимости следует всегда использовать макрос OpSIBLING(o), а не обращаться к полю op_sibling напрямую.
Другой способ просмотреть дерево — использовать модуль компилятора back-end, такой как B::Concise.
Этап компиляции 1: процедуры проверки
Дерево создаётся компилятором, пока код yacc предоставляет ему конструкции, которые он распознаёт. Поскольку yacc работает снизу вверх, так же происходит и первый этап компиляции Perl.
То, что делает этот этап интересным для разработчиков Perl, — это то, что на этом этапе может быть выполнена некоторая оптимизация. Это оптимизация с помощью так называемых "процедур проверки". Соответствие между именами узлов и соответствующими процедурами проверки описано в opcode.pl (не забудьте запустить make regen_headers, если вы изменяете этот файл).
Процедура проверки вызывается, когда узел полностью сформирован, за исключением потока порядка выполнения. Поскольку в этот момент нет обратных ссылок на текущий созданный узел, можно выполнить практически любую операцию над узлом верхнего уровня, включая освобождение и/или создание новых узлов над/под ним.
Процедура проверки возвращает узел, который должен быть вставлен в дерево (если узел верхнего уровня не был изменён, процедура проверки возвращает свой аргумент).
По соглашению, процедуры проверки имеют имена ck_*. Обычно они вызываются из подпрограмм new*OP (или convert) (которые, в свою очередь, вызываются из perly.y).
Этап компиляции 1a: свёртка констант
Сразу после вызова процедуры проверки возвращённый узел проверяется на возможность выполнения во время компиляции. Если это так (значение считается константой), оно немедленно выполняется, и вместо него подставляется узел константы со "значением возврата" соответствующего поддерева. Поддерево удаляется.
Если свёртка констант не была выполнена, создаётся поток порядка выполнения.
Этап компиляции 2: распространение контекста
Когда контекст для части дерева компиляции известен, он распространяется вниз по дереву. В этот момент контекст может иметь 5 значений (вместо 2 для контекста во время выполнения): пустое, булево, скалярное, списковое и lvalue. В отличие от этапа 1, этот этап обрабатывается сверху вниз: контекст узла определяет контекст его дочерних узлов.
В это время выполняются дополнительные оптимизации, зависящие от контекста. Поскольку на этом этапе дерево компиляции содержит обратные ссылки (через указатели "потока"), узлы нельзя освобождать (free()). Чтобы позволить оптимизированные узлы на этом этапе, такие узлы нуллируются (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 используется с DEBUGGING, а 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, которая выводит все подпрограммы в stash и дерево 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 фактически не скрывает все символы внутри большой глобальной структуры: некоторые vtable PerlIO_xxx остаются видимыми. PERL_GLOBAL_STRUCT_PRIVATE затем скрывает всё (см., как используется PERLIO_FUNCS_DECL).
Всё это, очевидно, требует способа для внутренних функций Perl быть либо подпрограммами, принимающими некоторый вид структуры в качестве первого аргумента, либо подпрограммами, не принимающими никакого первого аргумента. Для обеспечения этих двух очень разных способов построения интерпретатора исходный код Perl (как и во многих других ситуациях) широко использует макросы и соглашения об именовании подпрограмм.
Первая проблема: определение того, какие функции будут функциями публичного API, а какие — частными. Все функции, имена которых начинаются с S_, являются частными (подумайте "S" — "секретный" или "статический"). Все остальные функции начинаются с "Perl_", но только потому, что функция начинается с "Perl_", не означает, что она является частью API. (См. "Внутренние функции".) Наиболее простой способ убедиться, что функция является частью API, — это найти её запись в perlapi. Если она есть в perlapi, она является частью API. Если её нет, и вы считаете, что она должна быть (т. е., вам нужно это для вашего расширения), отправьте вопрос на https://github.com/Perl/perl5/issues, объяснив, почему вы считаете, что она должна быть.
Вторая проблема: должен быть синтаксис, чтобы одни и те же объявления и вызовы подпрограмм могли передавать структуру в качестве первого аргумента или не передавать ничего. Для решения этой проблемы подпрограммы называются и объявляются определённым образом. Вот типичный заголовок статической функции, используемой внутри Perl:
STATIC void
S_incline(pTHX_ char *s) STATIC становится "static" на C и может быть #define'd в пустое значение в некоторых конфигурациях в будущем.
Общедоступная функция (т. е., часть внутреннего API, но не обязательно разрешённая для использования в расширениях) начинается так:
void
Perl_sv_setiv(pTHX_ SV* dsv, IV num) pTHX_ — это один из ряда макросов (в perl.h), которые скрывают детали контекста интерпретатора. THX означает "поток", "это" или "вещь", в зависимости от случая. (И нет, Джордж Лукас не участвует. :-) Первый символ может быть 'p' для pрототипа, 'a' для aргумента или 'd' для dекларации, поэтому у нас есть pTHX, aTHX и dTHX, и их варианты.
Когда Perl строится без опций, устанавливающих PERL_IMPLICIT_CONTEXT, нет первого аргумента, содержащего контекст интерпретатора. Конечная нижняя черта в макросе pTHX_ указывает, что расширение макроса требует запятой после аргумента контекста, так как следуют другие аргументы. Если PERL_IMPLICIT_CONTEXT не определён, pTHX_ будет проигнорирован, и подпрограмма не будет прототипирована для принятия дополнительного аргумента. Форма макроса без конечной нижней черты используется, когда нет дополнительных явных аргументов.
Когда одна внутренняя функция Perl вызывает другую, она должна передать контекст. Это обычно скрывается с помощью макросов. Рассмотрим sv_setiv. Он расширяется во что-то вроде этого:
#ifdef PERL_IMPLICIT_CONTEXT
#define sv_setiv(a,b) Perl_sv_setiv(aTHX_ a, b)
/* can't do this for vararg functions, see below */
#else
#define sv_setiv Perl_sv_setiv
#endif Это работает хорошо, и означает, что авторы XS могут с радостью написать:
sv_setiv(foo, bar); и всё равно это будет работать во всех режимах, в которых мог быть скомпилирован Perl.
Однако это не работает так чисто для функций varargs, так как макросы подразумевают, что число аргументов известно заранее. Вместо этого нам либо нужно полностью их прописать, передавая aTHX_ в качестве первого аргумента (ядро Perl, как правило, делает это с функциями, подобными Perl_warner), либо использовать безконтекстную версию.
Бесконтекстная версия Perl_warner называется Perl_warner_nocontext и не принимает дополнительный аргумент. Вместо этого она использует dTHX; для получения контекста из локального хранилища потока. Мы #define warner Perl_warner_nocontext, чтобы расширения получили совместимость исходного кода за счёт производительности. (Передача аргумента обходится дешевле, чем получение его из локального хранилища потока.)
При просмотре заголовков/источников Perl вы можете игнорировать [pad]THXx. Они предназначены только для использования внутри ядра. Расширениям и встраивающим компонентам нужно знать только [pad]THX.
Что случилось с dTHR?
dTHR был введён в perl 5.005 для поддержки более старой модели потоков. Более старая модель потоков теперь использует механизм THX для передачи указателей контекста, поэтому dTHR больше не нужен. Perl 5.6.0 и более поздние версии всё ещё имеют его для обратной совместимости исходного кода, но он определён как бесполезная операция.
Как использовать всё это в расширениях?
Когда Perl скомпилирован с PERL_IMPLICIT_CONTEXT, расширения, которые вызывают любые функции API Perl, должны каким-то образом передавать начальный аргумент контекста. Суть в том, что вам нужно написать это таким образом, чтобы расширение всё ещё компилировалось, когда Perl не был скомпилирован с включённым PERL_IMPLICIT_CONTEXT.
Существует три способа сделать это. Во-первых, лёгкий, но неэффективный способ, который также является стандартным, чтобы сохранить совместимость исходного кода с расширениями: всякий раз, когда включается XSUB.h, он переопределяет макросы aTHX и aTHX_ для вызова функции, которая вернёт контекст. Таким образом, что-то вроде:
sv_setiv(sv, num); в вашем расширении будет переведено в это, когда PERL_IMPLICIT_CONTEXT активен:
Perl_sv_setiv(Perl_get_context(), sv, num); или в это в противном случае:
Perl_sv_setiv(sv, num); Вам ничего нового не нужно делать в расширении, чтобы это работало; так как библиотека Perl предоставляет Perl_get_context(), всё будет работать.
Второй, более эффективный способ, заключается в использовании следующей шаблона для вашего Foo.xs:
#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
STATIC void my_private_function(int arg1, int arg2);
STATIC void
my_private_function(int arg1, int arg2)
{
dTHX; /* fetch context */
... call many Perl API functions ...
}
[... etc ...]
MODULE = Foo PACKAGE = Foo
/* typical XSUB */
void
my_xsub(arg)
int arg
CODE:
my_private_function(arg, 10); Обратите внимание, что единственные два изменения по сравнению с обычным способом написания расширения — добавление #define PERL_NO_GET_CONTEXT перед включением заголовков Perl, за которым следует объявление dTHX; в начале каждой функции, которая будет вызывать API Perl. (Вы узнаете, какие функции нуждаются в этом, потому что компилятор C будет жаловаться на то, что в этих функциях неопределён идентификатор.) Изменения не нужны для самих XSUB, потому что макрос XS() правильно определён для передачи неявного контекста, если это необходимо.
Третий, ещё более эффективный способ — скопировать то, как это делается внутри ядра Perl:
#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
/* pTHX_ only needed for functions that call Perl API */
STATIC void my_private_function(pTHX_ int arg1, int arg2);
STATIC void
my_private_function(pTHX_ int arg1, int arg2)
{
/* dTHX; not needed here, because THX is an argument */
... call Perl API functions ...
}
[... etc ...]
MODULE = Foo PACKAGE = Foo
/* typical XSUB */
void
my_xsub(arg)
int arg
CODE:
my_private_function(aTHX_ arg, 10); В этой реализации никогда не нужно получать контекст с помощью вызова функции, так как он всегда передаётся в качестве дополнительного аргумента. В зависимости от ваших потребностей в простоте или эффективности, вы можете смешивать предыдущие два подхода.
Никогда не добавляйте запятую после pTHX сами — всегда используйте форму макроса с нижним подчёркиванием для функций, принимающих явные аргументы, или форму без аргумента для функций без явных аргументов.
Если вы компилируете Perl с -DPERL_GLOBAL_STRUCT, необходимо определить dVAR, если в функции обращаются к глобальным переменным Perl (см. perlvars.h или globvar.sym) и dTHX не используется (dTHX включает dVAR при необходимости). Необходимость в dVAR заметна только при указанном определении времени компиляции, потому что в противном случае глобальные переменные Perl видны как есть.
Нужно ли что-то особенное делать, если я вызываю perl из нескольких потоков?
Если вы создаёте интерпретаторы в одном потоке, а затем вызываете их в другом, вам нужно убедиться, что слот локального хранилища потоков (TLS) perl инициализирован правильно в каждом из этих потоков.
Функции API perl_alloc и perl_clone автоматически установят слот TLS в интерпретатор, который они создали, поэтому нет необходимости в чём-то особенном, если интерпретатор всегда используется в том же потоке, который его создал, и этот поток не создавал или не вызывал никаких других интерпретаторов после этого. Если это не так, вы должны установить слот TLS потока перед вызовом любых функций API Perl для этого конкретного интерпретатора. Это делается путём вызова макроса PERL_SET_CONTEXT в этом потоке в качестве первой вещи, которую вы делаете:
/* do this before doing anything else with some_perl */
PERL_SET_CONTEXT(some_perl);
... other Perl API calls on some_perl go here ... Будущие планы и PERL_IMPLICIT_SYS
Так же как PERL_IMPLICIT_CONTEXT предоставляет способ объединить всё, что интерпретатор знает о себе, и передавать это, так же планируется позволить интерпретатору объединить всё, что он знает об окружающей среде, в которой он работает. Это активируется макросом PERL_IMPLICIT_SYS. В настоящее время он работает только с USE_ITHREADS в Windows.
Это позволяет предоставить дополнительный указатель (называемый «средой хоста») для всех системных вызовов. Это позволяет всем системным функциям сохранять собственное состояние, разбитое на семь структур C. Это тонкие оболочки вокруг обычных системных вызовов (см. win32/perllib.c) для исполняемого файла perl по умолчанию, но для более амбициозного хоста (например, того, который бы имитировал fork()) вся дополнительная работа, необходимая для того, чтобы симулировать, что разные интерпретаторы — на самом деле разные «процессы», будет сделана здесь.
Двигатель/интерпретатор Perl и хост — ортогональные сущности. В одном процессе может быть один или несколько интерпретаторов и один или несколько «хостов», с произвольным соединением между ними.
Внутренние функции
Все внутренние функции Perl, которые будут доступны внешнему миру, имеют префикс Perl_, чтобы они не конфликтовали с функциями XS или функциями, используемыми в программе, в которую встроен Perl. Аналогично, все глобальные переменные начинаются с PL_. (По соглашению, статические функции начинаются с S_.)
Внутри ядра Perl (PERL_CORE определён), вы можете получить доступ к функциям либо с префиксом Perl_, либо без него, благодаря множеству определений, которые находятся в embed.h. Обратите внимание, что код расширения не должен устанавливать PERL_CORE; это раскрывает все внутренности perl и, скорее всего, приведёт к разрыву XS в каждой новой версии perl.
Файл embed.h автоматически генерируется из embed.pl и embed.fnc. embed.pl также создаёт прототипирующие заголовочные файлы для внутренних функций, генерирует документацию и множество других деталей. Важно, что при добавлении новой функции в ядро или изменении существующей функции вы также должны изменить данные в таблице в embed.fnc. Вот пример записи из этой таблицы:
Apd |SV** |av_fetch |AV* ar|I32 key|I32 lval Второй столбец — тип возвращаемого значения, третий — имя. Столбцы после этого — аргументы. Первый столбец — набор флагов:
- 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 Encodings» в 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 Rules versus Unicode Rules» в perlunicode). Это проблема только для символов, чьи порядковые номера находятся между 128 и 255, и их поведение меняется при использовании ASCII по сравнению с правилами Unicode способами, которые важны для вашего кода (см. «The "Unicode Bug"» в perlunicode). Нет опубликованного API для работы с этим, так как он может измениться, но вы можете посмотреть на код pp_lc в pp.c, чтобы увидеть, как это делается в настоящее время.
Как преобразовать строку в UTF-8?
Если вы смешиваете строки UTF-8 и не UTF-8, необходимо обновить строки, не являющиеся UTF-8, до UTF-8. Если у вас есть SV, самый простой способ сделать это:
sv_utf8_upgrade(sv); Однако, вы не должны делать это, например:
if (!SvUTF8(left))
sv_utf8_upgrade(left); Если вы делаете это в бинарной операции, вы фактически измените одну из строк, которые были переданы в операцию, и, хотя это не должно быть заметно для конечного пользователя, это может вызвать проблемы в несовершенном коде.
Вместо этого bytes_to_utf8 предоставит вам закодированную в UTF-8 копию своего аргумента-строки. Это полезно для того, чтобы данные были доступны для сравнений и т.д., без повреждения исходного SV. Также есть utf8_to_bytes для обратного преобразования, но, естественно, это потерпит неудачу, если строка содержит символы с кодами больше 255, которые нельзя представить в одном байте.
Как сравнивать строки?
"sv_cmp" в perlapi и "sv_cmp_flags" в perlapi выполняют лексическое сравнение двух SV, правильно обрабатывая UTF-8. Однако обратите внимание, что Unicode определяет более сложный механизм сортировки, доступный через модуль Unicode::Collate.
Для простого сравнения двух строк на равенство/неравенство можно использовать memEQ() и memNE(), как обычно, но строки должны быть закодированы либо как UTF-8, либо не как UTF-8.
Для сравнения двух строк без учета регистра используйте foldEQ_utf8() (строки не должны иметь одинаковый формат UTF-8).
Нужно ли мне знать что-то еще?
В общем-то, нет. Просто запомните следующие моменты:
-
Нет способа определить, является ли строка
char *илиU8 *строкой UTF-8 или нет. Однако можно определить, должна ли переменная SV обрабатываться как UTF-8, вызвавDO_UTF8для неё после преобразования в строку с помощьюSvPVили аналогичного макроса. Кроме того, вы можете определить, является ли SV фактически UTF-8 (даже если она не должна обрабатываться как такая), посмотрев на её флагSvUTF8(снова после преобразования в строку). Не забудьте установить флаг, если что-то должно быть UTF-8. Считайте флаг частью PV, даже если это не так — если вы передаёте PV куда-то, передавайте и флаг. -
Если строка является UTF-8, всегда используйте
utf8_to_uvchr_bufдля получения значения, еслиUTF8_IS_INVARIANT(*s), в противном случае можно использовать*s. -
При записи символа UV в строку UTF-8 всегда используйте
uvchr_to_utf8, еслиUVCHR_IS_INVARIANT(uv)), в противном случае можно использовать*s = uv. -
Смешивание строк UTF-8 и не-UTF-8 сложно. Используйте
bytes_to_utf8для получения новой строки с кодировкой UTF-8, а затем комбинируйте их.
Пользовательские операторы
Поддержка пользовательских операторов — экспериментальная функция, позволяющая определять собственные операции. Это в первую очередь предназначено для построения интерпретаторов других языков в ядре Perl, но также позволяет оптимизировать код с помощью создания "макро-операций" (операций, которые выполняют функции нескольких операций, обычно выполняемых вместе, таких как gvsv, gvsv, add).
Эта функция реализована как новый тип операции, OP_CUSTOM. Ядро Perl не "знает" ничего особенного об этом типе операций, поэтому оно не будет участвовать в каких-либо оптимизациях. Это также означает, что вы можете определить свои пользовательские операции как любую структуру операций — унарную, бинарную, списочную и т. д., которая вам нравится.
Важно знать, чего не смогут сделать пользовательские операторы. Они не позволят вам напрямую добавлять новый синтаксис в Perl. Они даже не позволят напрямую добавить новые ключевые слова. На самом деле, они вообще не изменят способ компиляции программы Perl. Вам придётся внести эти изменения самостоятельно после того, как Perl скомпилирует программу. Вы делаете это, либо манипулируя деревом операций с помощью блока CHECK и модуля B::Generate, либо добавляя пользовательский оптимизатор с помощью модуля optimize.
При этом обычные операции Perl заменяются пользовательскими операциями, создавая операции с типом OP_CUSTOM и op_ppaddr вашей собственной функции PP. Это должно быть определено в 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при обнаружении операций этого типа оптимизатором peephole. o — это операция, требующая оптимизации; oldop — предыдущая оптимизированная операция, чья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) потребуют выталкивания контекстов до тех пор, пока не будет найден контекст eval. Оба этих действия выполняются с помощью dounwind(), способного обрабатывать и выталкивать все контексты, расположенные выше целевого.
Вот типичный пример выталкивания контекста, как он встречается в pp_leavesub (несколько упрощённый):
U8 gimme;
PERL_CONTEXT *cx;
SV **oldsp;
OP *retop;
cx = CX_CUR();
gimme = cx->blk_gimme;
oldsp = PL_stack_base + cx->blk_oldsp; /* last arg of previous frame */
if (gimme == G_VOID)
PL_stack_sp = oldsp;
else
leave_adjust_stacks(oldsp, oldsp, gimme, 0);
CX_LEAVE_SCOPE(cx);
cx_popsub(cx);
cx_popblock(cx);
retop = cx->blk_sub.retop;
CX_POP(cx);
return retop; Перечисленные выше шаги следуют очень определённому порядку, предназначенному для обращения по порядку, в котором был помещён контекст. Первое, что нужно сделать, — это скопировать и/или защитить все аргументы возврата и освободить все временные переменные в текущей области видимости. Выход из области видимости, подобно подпрограмме rvalue, обычно возвращает смертельную копию своих аргументов возврата (в отличие от подпрограмм lvalue). Важно сделать эту копию до того, как стек сохранений будет вытолкнуто или переменные восстановлены, иначе могут произойти такие неприятные вещи:
sub f { my $x =...; $x } # $x freed before we get to copy it
sub f { /(...)/; $1 } # PL_curpm restored before $1 copied Хотя мы хотели бы освободить все временные переменные в то же время, нам нужно быть осторожными, чтобы не освободить временные переменные, которые поддерживают аргументы возврата; и не освобождать временные переменные, которые мы только что создали, копируя аргументы возврата.
К счастью, leave_adjust_stacks() способен создавать смертельные копии аргументов возврата, перемещая аргументы вниз по стеку и обрабатывая только те записи в стеке временных переменных, которые безопасно сделать.
В контексте void не возвращаются аргументы, поэтому эффективнее пропустить вызов leave_adjust_stacks(). Также в контексте void операция nextstate, вероятно, будет вызвана незамедлительно, что выполнит FREETMPS, поэтому нет необходимости делать это.
Следующим шагом является выталкивание элементов стека сохранений: CX_LEAVE_SCOPE(cx) просто определён как LEAVE_SCOPE(cx->blk_oldsaveix). Обратите внимание, что во время выталкивания Perl может вызвать деструкторы, вызвать STORE для отмены локализации связанных переменных и так далее. Любой из этих элементов может завершиться или вызвать exit(). В этом случае будет вызван dounwind(), и текущая кадрная рамка стека контекста будет обработана повторно. Таким образом, крайне важно, чтобы все шаги выталкивания контекста выполнялись таким образом, чтобы поддерживать возможность повторного входа. Другой вариант — уменьшить cxstack_ix до обработки кадра, — приведёт к утечкам и т. п., если что-то завершится в середине или будет перезаписано текущий кадр.
Сам CX_LEAVE_SCOPE безопасно поддерживает повторный вход: если перед завершением и поимкой ловушкой eval были вытолкнуты только половина элементов стека сохранений, то CX_LEAVE_SCOPE в dounwind или pp_leaveeval будут продолжены с того места, где остановился первый.
Следующим шагом является обработка контекста, специфичная для типа; в данном случае cx_popsub. Частично это выглядит следующим образом:
cv = cx->blk_sub.cv;
CvDEPTH(cv) = cx->blk_sub.olddepth;
cx->blk_sub.cv = NULL;
SvREFCNT_dec(cv); где он обрабатывает только что выполненный CV. Обратите внимание, что перед уменьшением счётчика ссылок CV он сбрасывает blk_sub.cv. Это означает, что если он повторно входит, CV не будет освобождён дважды. Это также означает, что вы не можете полагаться на то, что такие поля, специфичные для типа, будут иметь полезные значения после возврата из cx_popfoo.
Далее, cx_popblock восстанавливает все различные переменные интерпретатора до их предыдущих значений или предыдущих максимальных значений; это расширяется до:
PL_markstack_ptr = PL_markstack + cx->blk_oldmarksp;
PL_scopestack_ix = cx->blk_oldscopesp;
PL_curpm = cx->blk_oldpm;
PL_curcop = cx->blk_oldcop;
PL_tmps_floor = cx->blk_old_tmpsfloor; Обратите внимание, что он не восстанавливает PL_stack_sp; как упоминалось ранее, какое значение восстановить зависит от типа контекста (в частности, for (list) {}) и каких-либо возвращаемых аргументов (если таковые имеются); и это уже будет отсортировано ранее leave_adjust_stacks().
Наконец, указатель стека контекста фактически уменьшается с помощью CX_POP(cx). После этого момента существует вероятность того, что текущая кадрная рамка контекста может быть перезаписана другими контекстами, которые помещаются в стек. Хотя такие вещи, как привязки и DESTROY, предполагается, работают в новом контексте стека, лучше всего не делать этого предположения. Действительно, в сборках отладки CX_POP(cx) намеренно устанавливает cx в null, чтобы обнаружить код, который всё ещё полагается на значения полей в этой кадрной рамке контекста. Обратите внимание в примере pp_leavesub() выше, что мы получаем blk_sub.retop до вызова CX_POP.
Повторное выполнение контекстов
Наконец, есть cx_topblock(cx), который действует как супер-nextstate, что касается сброса различных переменных до их базовых значений. Он используется в таких местах, как pp_next, pp_redo и pp_goto, где вместо выхода из области видимости мы хотим перезапустить область видимости. Помимо сброса PL_stack_sp, как и nextstate, он также сбрасывает PL_markstack_ptr, PL_scopestack_ix и PL_curpm. Обратите внимание, что он не выполняет FREETMPS.
Выделение операторов на основе фрагментов
Примечание: этот раздел описывает неопубликованный внутренний API, который может быть изменён без предварительного уведомления.
Внутренние механизмы обработки ошибок Perl реализуют die (и его внутренние аналоги) с использованием longjmp. Если это произойдёт во время лексического анализа, синтаксического анализа или компиляции, мы должны убедиться, что все операторы, выделенные в рамках процесса компиляции, освобождены. (В более старых версиях Perl эта ситуация не обрабатывалась должным образом: при сбое синтаксического анализа они утекали операторы, хранившиеся в C-переменных auto, не связанные ни с чем другим.)
Для обработки этой ситуации Perl использует фрагменты операторов, прикреплённые к текущему CV, который компилируется. Фрагмент — это блок выделенной памяти. Новые операторы выделяются как области фрагмента. Если фрагмент заполняется, создаётся новый (и связывается со старым). Когда происходит ошибка, а CV освобождается, все оставшиеся операторы освобождаются.
Каждый оператор предваряется двумя указателями: один указывает на следующий оператор в фрагменте, а другой — на фрагмент, которому он принадлежит. Указатель следующего оператора необходим для того, чтобы Perl мог перебирать фрагмент и освобождать все его операторы. (Структуры операторов имеют разный размер, поэтому операторы фрагмента не могут просто рассматриваться как плотный массив.) Указатель фрагмента необходим для доступа к счётчику ссылок фрагмента: когда последний оператор в фрагменте освобождается, сам фрагмент освобождается.
Блокировщик фрагментов помещает операторы в конец фрагмента в первую очередь. Это будет способствовать выделению листьев дерева операторов в первую очередь, и, следовательно, расположение, вероятно, будет дружественным к кэшу. Кроме того, это означает, что нет необходимости хранить размер фрагмента (см. ниже, почему фрагменты имеют разный размер), поскольку Perl может следовать указателям, чтобы найти последний оператор.
Возможно, казалось бы возможным устранить счётчики ссылок фрагментов, сделав все операторы неявными прикреплёнными к PL_compcv при выделении и освобождении при освобождении CV. Это также позволило бы op_free пропустить FreeOp, и, следовательно, быстрее освободить операторы. Но это не работает в тех случаях, когда операторы должны выжить за пределами своих CV, например, при повторных вычислениях.
CV также должен иметь счётчик ссылок на фрагмент. Иногда первый созданный оператор сразу же освобождается. Если счётчик ссылок фрагмента достигнет 0, он будет освобождён, даже если CV по-прежнему указывает на него.
CV использует флаг CVf_SLABBED, чтобы указать, что CV имеет счётчик ссылок на фрагмент. Когда этот флаг установлен, фрагмент доступен через CvSTART, когда CvROOT не установлен, или путём вычитания двух указателей (2*sizeof(I32 *)) из CvROOT, когда он установлен. Альтернативой этому подходу, в котором фрагмент проникает в CvSTART во время компиляции, было бы увеличение размера структуры xpvcv на ещё один указатель. Но это сделает все CV больше, даже если освобождение операторов на основе фрагментов обычно полезно только для программ, которые активно используют строку eval.
Когда установлен флаг CVf_SLABBED, CV берёт на себя ответственность за освобождение фрагмента. Если CvROOT не установлен при освобождении или отмене определения CV, предполагается, что произошла ошибка компиляции, поэтому фрагмент операторов перебирается, и все операторы освобождаются.
В нормальных условиях CV забывает о своём фрагменте (уменьшая счётчик ссылок) при присоединении корня. Таким образом, подсчёт ссылок на фрагмент, который происходит при освобождении операторов, заботится об освобождении фрагмента. В некоторых случаях CV получает указание забыть о фрагменте (cv_forget_slab) именно для того, чтобы операторы могли выжить после того, как CV будет устранён.
Пропуск фрагмента памяти (slab) при подключении корня не является строго необходимым, но позволяет избежать потенциальных проблем с CvROOT перезаписыванием. Код, как в ядре, так и на CPAN, часто работает с CvROOT, поэтому пропуск фрагмента памяти повышает надёжность и предотвращает потенциальные проблемы.
Так как контрольный блок (CV) принимает во владение свой фрагмент памяти (slab) при установке флага, этот флаг никогда не копируется при клонировании CV, поскольку один CV может освободить фрагмент памяти, на который ещё ссылается другой CV, так как принудительное освобождение операций игнорирует счётчик ссылок (но проверяет, что всё правильно).
Для предотвращения фрагментации фрагмента памяти, освобождённые операции помечаются как освобождённые и прикрепляются к цепочке освобождённых фрагментов памяти (идея позаимствована у DBM::Deep). Эти освобождённые операции повторно используются, если это возможно. Не использовать повторно освобождённые операции было бы проще, но это привело бы к значительному увеличению потребления памяти для программ с большими if (DEBUG) {...} блоками.
SAVEFREEOP немного проблематично в этой схеме. Иногда это может привести к тому, что операция будет освобождена после её CV. Если CV принудительно освободил операции в своём фрагменте памяти (slab) и сам фрагмент памяти, то мы будем работать с освобождённым фрагментом памяти. Превращение SAVEFREEOP в пустую операцию не помогает, так как иногда операция может быть освобождена без ошибки компиляции, поэтому операция никогда не будет освобождена. Она хранит счётчик ссылок на фрагмент памяти, поэтому весь фрагмент памяти будет утечь. Поэтому SAVEFREEOP сейчас устанавливает специальный флаг на операции (->op_savefree). Принудительное освобождение операций после ошибки компиляции не освободит никакие операции, помеченные таким образом.
Поскольку многие части кода создают крошечные подпрограммы, состоящие всего из нескольких операций, и поскольку большой фрагмент памяти (slab) был бы значительной нагрузкой для них, первый фрагмент памяти всегда очень мал. Чтобы избежать выделения слишком большого количества фрагментов памяти для одного контрольного блока (CV), каждый последующий фрагмент памяти имеет размер в два раза больше предыдущего.
Smartmatch ожидает возможность выделения операции во время выполнения, её выполнения и удаления. Для этого операция просто выделяется с помощью malloced, когда PL_compcv не был настроен. Таким образом, все выделенные фрагментом памяти операции помечаются как таковые (->op_slabbed), чтобы отличить их от выделенных malloced операций.
АВТОРЫ
До мая 1997 года этот документ поддерживал Джефф Окамото <okamoto@corp.hp.com>. Сейчас он поддерживается в рамках Perl самим Perl 5 Портерами <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.30.3/perlguts