perlguts
СОДЕРЖАНИЕ
- ИМЯ
- ОПИСАНИЕ
- Переменные
- Типы данных
- Что такое "IV"?
- Работа с SVs
- Смещения
- Что на самом деле хранится в SV?
- Работа с AVs
- Работа с HVs
- Расширения API хешей
- AVs, HVs и неопределённые значения
- Ссылки
- Благословенные ссылки и объекты классов
- Создание новых переменных
- Счётчики ссылок и смертность
- Стек и Глобы
- Дескрипторы ввода-вывода
- SVs с двойным типом
- Значения только для чтения
- Копирование при записи
- Магические переменные
- Назначение магических свойств
- Магические виртуальные таблицы
- Поиск магических свойств
- Понимание магии связанных хешей и массивов
- Локализация изменений
- Подпрограммы
- Выделение памяти
- PerlIO
- Компилируемый код
- Просмотр внутренних структур данных с помощью функций dump
- Как поддерживаются несколько интерпретаторов и одновременность
- Внутренние функции
- Форматированный вывод IVs, UVs и NVs
- Форматированный вывод SVs
- Форматированный вывод строк
- Форматированный вывод Size_t и SSize_t
- Форматированный вывод Ptrdiff_t, intmax_t, short и других специальных размеров
- Указатель на целое число и целое число на указатель
- Обработка исключений
- Документация источника
- Обратная совместимость
- Поддержка Unicode
- Пользовательские операторы
- Стек
- Динамическая область видимости и стек контекстов
- Выделение операторов на основе сегментов
- АВТОРЫ
- СМОТРИТЕ ТАКЖЕ
ИМЯ
perlguts - Введение в Perl API
ОПИСАНИЕ
В данном документе пытаются описать, как использовать Perl API, а также предоставить некоторую информацию о базовой работе ядра Perl. Он далек от полного и, вероятно, содержит много ошибок. Пожалуйста, направляйте любые вопросы или комментарии автору ниже.
Переменные
Типы данных
Perl использует три определения типов, которые обрабатывают три основных типа данных Perl:
SV Scalar Value
AV Array Value
HV Hash Value Для каждого определения типа существуют специальные процедуры, которые манипулируют различными типами данных.
Что такое "IV"?
Perl использует специальное определение типа IV, которое является простым знаковым целочисленным типом, гарантированно достаточно большим, чтобы содержать указатель (а также целое число). Кроме того, существует UV, которое просто является беззнаковым IV.
Perl также использует несколько специальных определений типов для объявления переменных, хранящих целые числа (по крайней мере) заданного размера. Используйте I8, I16, I32 и I64 для объявления знакомой целочисленной переменной, в которой должно быть не меньше битов, чем в имени. Все они оцениваются как родной тип C, наиболее близкий к заданному количеству битов, но не меньше его. Например, на многих платформах short имеет длину 16 бит, и если так, I16 будет означать short. Но на платформах, где short не совсем 16 бит, Perl будет использовать наименьший тип, содержащий 16 бит или более.
U8, U16, U32 и U64 объявляют соответствующие беззнаковые целочисленные типы.
Если платформа не поддерживает 64-битные целые числа, как I64, так и U64 будут неопределенными. Используйте IV и UV для объявления максимально возможных, а "WIDEST_UTYPE" in perlapi для абсолютного максимального беззнакового, который может быть неиспользуемым во всех обстоятельствах.
Числовая константа может быть указана с помощью "INT16_C" в perlapi, "UINTMAX_C" в perlapi и аналогичных.
Работа с 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, но позволяет указать либо указатель на список переменных аргументов, либо адрес и длину массива SV. Последний аргумент указывает на логическое значение; при возврате, если это логическое значение истинно, то для форматирования строки использовалась информация, зависящая от локали, и содержимое строки, следовательно, недостоверно (см. perlsec). Этот указатель может быть NULL, если эта информация не важна. Обратите внимание, что эта функция требует указания длины формата.
Функции sv_set*() недостаточно универсальны для работы со значениями, имеющими «магию». См. "Магические виртуальные таблицы" далее в этом документе.
Все SV, содержащие строки, должны завершаться символом NUL. Если строка не завершается символом NUL, существует риск сбойных ситуаций и повреждения данных из-за кода, передающего строку функциям C или системным вызовам, ожидающим строку, завершенную символом NUL. Собственные функции Perl обычно добавляют заключительный символ NUL по этой причине. Тем не менее, вы должны быть очень осторожны, когда передаете строку, хранящуюся в SV, функции C или системному вызову.
Для доступа к фактическому значению, на которое указывает SV, API Perl предоставляет несколько макросов, которые преобразуют фактический скалярный тип в IV, UV, double или строку:
-
SvIV(SV*)(IV) иSvUV(SV*)(UV) -
SvNV(SV*)(double) -
Строки немного сложнее:
-
Строка байтов:
SvPVbyte(SV*, STRLEN len)илиSvPVbyte_nolen(SV*)Если Perl-строка — это
"\xff\xff", то это возвращает 2-байтовыйchar*.Это подходит для Perl-строк, представляющих байты.
-
Строка UTF-8:
SvPVutf8(SV*, STRLEN len)илиSvPVutf8_nolen(SV*)Если Perl-строка — это
"\xff\xff", то это возвращает 4-байтовыйchar*.Это подходит для Perl-строк, представляющих символы.
ЗАМЕЧАНИЕ: Что
char*будет закодировано с использованием внутренней кодировки UTF-8 Perl, что означает, что если SV содержит не-Unicode символы (например, 0x110000), то результат может содержать расширения, выходящие за рамки валидного UTF-8. См. "is_strict_utf8_string" в perlapi для методов проверки валидности UTF-8 этих макросов. -
Вы также можете использовать
SvPV(SV*, STRLEN len)илиSvPV_nolen(SV*)для извлечения исходного внутреннего буфера SV. Однако это сложно; если ваша Perl-строка —"\xff\xff", то в зависимости от внутренней кодировки SV вы можете получить 2-байтовый ИЛИ 4-байтовыйchar*. Более того, если это 4-байтовая строка, она может исходить как из Perl-строки"\xff\xff"в кодировке UTF-8, так и из Perl-строки"\xc3\xbf\xc3\xbf"в виде исходных октетов. Чтобы отличить эти варианты, НЕОБХОДИМО проверить бит UTF8 SV (см.SvUTF8), чтобы узнать, является ли исходная Perl-строка 2-символьной (SvUTF8будет включён) или 4-символьной (SvUTF8будет выключен).ВАЖНО: Использование
SvPV,SvPV_nolen, или аналогичных макросов без проверки бита UTF8 SV почти наверняка является ошибкой, если допускается входной не-ASCII текст.Когда бит UTF8 установлен, к результату применим тот же ЗАМЕЧАНИЕ о валидности UTF-8, что и для
SvPVutf8.
(См. "Как передать Perl-строку в C-библиотеку?" для получения более подробной информации.)
В
SvPVbyte,SvPVutf8, иSvPV, длина возвращаемойchar*помещается в переменнуюlen(это макросы, поэтому вы не используете&len). Если вам не важна длина данных, используйтеSvPVbyte_nolen,SvPVutf8_nolen, илиSvPV_nolenвместо этого. Глобальная переменнаяPL_naтакже может быть передана вSvPVbyte/SvPVutf8/SvPVв этом случае. Но это может быть довольно неэффективно, так как наPL_naнужно получить доступ в локальном хранилище потоков в многопоточном Perl. В любом случае, помните, что Perl допускает произвольные строки данных, которые могут содержать NUL-символы и могут не завершаться символомNUL.Также помните, что C не позволяет вам безопасно делать
foo(SvPVbyte(s, len), len);. Это может работать с вашим компилятором, но не со всеми. Разбейте такое утверждение на отдельные присваивания:SV *s; STRLEN len; char *ptr; ptr = SvPVbyte(s, len); foo(ptr, len); -
Если вы хотите узнать, является ли скалярное значение TRUE, вы можете использовать:
SvTRUE(SV*) Хотя Perl автоматически увеличивает размер строк, если вам нужно принудительно выделить больше памяти для вашего SV, вы можете использовать макрос
SvGROW(SV*, STRLEN newlen) который определит, нужно ли выделять больше памяти. В таком случае он вызовет функцию sv_grow. Обратите внимание, что SvGROW может только увеличивать, а не уменьшать, выделенную память SV и не автоматически добавляет место для заключительного NUL байта (собственные функции строк Perl обычно SvGROW(sv, len + 1)).
Если вы хотите записать в буфер существующего SV и установить его значение в строку, используйте SvPVbyte_force() или одну из его разновидностей, чтобы принудительно сделать SV типом PV. Это удалит различные типы не-строковости из SV, сохранив содержимое SV в PV. Это может быть использовано, например, для добавления данных из функции API в буфер без дополнительной копирования:
(void)SvPVbyte_force(sv, len);
s = SvGROW(sv, len + needlen + 1);
/* something that modifies up to needlen bytes at s+len, but
modifies newlen bytes
eg. newlen = read(fd, s + len, needlen);
ignoring errors for these examples
*/
s[len + newlen] = '\0';
SvCUR_set(sv, len + newlen);
SvUTF8_off(sv);
SvSETMAGIC(sv); Если данные уже находятся в памяти или вы хотите упростить код, вы можете использовать один из вариантов sv_cat*(), таких как sv_catpvn(). Если вы хотите вставить данные в любое место строки, вы можете использовать sv_insert() или sv_insert_flags().
Если вам не нужно существующее содержимое SV, вы можете избежать копирования с помощью:
SvPVCLEAR(sv);
s = SvGROW(sv, needlen + 1);
/* something that modifies up to needlen bytes at s, but modifies
newlen bytes
eg. newlen = read(fd, s, needlen);
*/
s[newlen] = '\0';
SvCUR_set(sv, newlen);
SvPOK_only(sv); /* also clears SVf_UTF8 */
SvSETMAGIC(sv); Опять же, если данные уже находятся в памяти или вы хотите избежать сложности выше, вы можете использовать sv_setpvn().
Если у вас есть буфер, выделенный с помощью Newx(), и вы хотите установить его как значение SV, вы можете использовать sv_usepvn_flags(). Это имеет некоторые требования, если вы хотите избежать повторного выделения памяти perl для добавления заключительного NUL:
Newx(buf, somesize+1, char);
/* ... fill in buf ... */
buf[somesize] = '\0';
sv_usepvn_flags(sv, buf, somesize, SV_SMAGIC | SV_HAS_TRAILING_NUL);
/* buf now belongs to perl, don't release it */ Если у вас есть SV и вы хотите узнать, какой тип данных Perl считает хранящимся в нём, вы можете использовать следующие макросы для проверки типа SV.
SvIOK(SV*)
SvNOK(SV*)
SvPOK(SV*) Обратите внимание, что получение числового значения SV может установить IOK или NOK для этого SV, даже если SV изначально был строкой. До Perl 5.36.0 получение строкового значения целого числа могло установить POK, но это больше не происходит. С версии 5.36.0 это можно использовать для различения исходного представления SV и призвано упростить работу сериализаторов:
/* references handled elsewhere */
if (SvIsBOOL(sv)) {
/* originally boolean */
...
}
else if (SvPOK(sv)) {
/* originally a string */
...
}
else if (SvNIOK(sv)) {
/* originally numeric */
...
}
else {
/* something special or undef */
} Вы можете получить и установить текущую длину строки, хранящейся в 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. Вы можете указать адрес и длину массива SV вместо аргумента 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 с &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, которые содержат логические значения TRUE и FALSE соответственно. Как и 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. Поскольку скаляр может быть и числом, и строкой, эти макросы обычно всегда возвращают TRUE, а вызов макросов 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.
Работа с AV
Существует два основных, давно используемых способа создания и загрузки AV. Первый метод создает пустой AV:
AV* newAV(); Второй метод создает AV и сразу заполняет его SV:
AV* av_make(SSize_t num, SV **ptr); Второй аргумент указывает на массив, содержащий num SV*. После создания AV SVs можно уничтожить, если это необходимо.
Perl v5.36 добавил два новых способа создания AV и выделения массива SV**, не заполняя его. Они более эффективны, чем newAV() за которым следует av_extend().
/* Creates but does not initialize (Zero) the SV** array */
AV *av = newAV_alloc_x(1);
/* Creates and does initialize (Zero) the SV** array */
AV *av = newAV_alloc_xz(1); Числовой аргумент относится к количеству элементов массива для выделения, а не к индексу массива, и должен быть >0. Первую форму следует использовать только в том случае, если все элементы будут инициализированы до любого чтения. Чтение неинициализированного SV* — т. е. обработка случайного адреса памяти как SV* — является серьезной ошибкой.
После создания AV возможны следующие операции с ним:
void av_push(AV*, SV*);
SV* av_pop(AV*);
SV* av_shift(AV*);
void av_unshift(AV*, SSize_t num); Эти операции должны быть знакомы, за исключением av_unshift. Эта процедура добавляет num элементов в начало массива со значением undef. Затем необходимо использовать av_store (описано ниже), чтобы присвоить значения этим новым элементам.
Вот несколько других функций:
SSize_t av_top_index(AV*);
SV** av_fetch(AV*, SSize_t key, I32 lval);
SV** av_store(AV*, SSize_t key, SV* val); Функция av_top_index возвращает наибольшее значение индекса в массиве (точно так же, как $#array в Perl). Если массив пуст, возвращается -1. Функция av_fetch возвращает значение по индексу key, но если lval отлично от нуля, то av_fetch сохранит значение undef в этом индексе. Функция av_store сохраняет значение val в индексе key, и не увеличивает счетчик ссылок val. Таким образом, вызывающая сторона отвечает за это, и если av_store возвращает NULL, вызывающая сторона должна уменьшить счетчик ссылок, чтобы избежать утечки памяти. Обратите внимание, что av_fetch и av_store обе возвращают SV** , а не SV* в качестве возвращаемого значения.
Еще несколько:
void av_clear(AV*);
void av_undef(AV*);
void av_extend(AV*, SSize_t key); Функция av_clear удаляет все элементы в массиве AV*, но не удаляет сам массив. Функция av_undef удаляет все элементы в массиве плюс сам массив. Функция av_extend расширяет массив так, чтобы он содержал как минимум key+1 элементов. Если key+1 меньше текущей длины выделенного массива, ничего не происходит.
Если вам известна переменная массива, вы можете получить указатель на ее AV, используя следующее:
AV* get_av("package::varname", 0); Это возвращает NULL, если переменная не существует.
См. "Понимание магии привязанных хешей и массивов" для получения дополнительной информации о том, как использовать функции доступа к массивам в привязанных массивах.
Более эффективная работа с новыми или простыми AV
В Perl v5.36 и v5.38 были введены оптимизированные, встроенные версии некоторых функций:
-
av_store_simple -
av_fetch_simple -
av_push_simple
Это прямые замены, но их можно использовать только с простыми AV, которые соответствуют следующим критериям:
-
не являются магическими
-
не являются только для чтения
-
являются «реальными» (со счетчиком ссылок) AV
-
имеют значение av_top_index > -2
AV, созданные с помощью newAV(), av_make, newAV_alloc_x, и newAV_alloc_xz, совместимы при создании. Они совместимы только тогда, когда они не объявлены только для чтения или нереальными, к ним не прикреплена магия или они не настроены необычным образом.
Обратите внимание, что некоторые функции интерпретатора могут прикрепить магию к AV в ходе обычных операций. Поэтому для того, чтобы не ошибиться, если вы не уверены в жизненном цикле AV, лучше использовать эти новые функции как можно ближе к моменту создания AV.
Работа с HVs
Для создания HV используется следующая процедура:
HV* newHV(); После создания HV возможны следующие операции с ним:
SV** hv_store(HV*, const char* key, U32 klen, SV* val, U32 hash);
SV** hv_fetch(HV*, const char* key, U32 klen, I32 lval); Параметр klen — это длина передаваемого ключа (Обратите внимание, что вы не можете передать 0 как значение klen, чтобы сказать Perl, что нужно измерить длину ключа). Аргумент val содержит указатель SV на скаляр, который хранится, а hash — предварительно вычисленный хеш-код (ноль, если вы хотите, чтобы hv_store рассчитал его за вас). Параметр lval указывает, является ли это извлечение частью операции сохранения, в этом случае в HV будет добавленное новое неопределенное значение с указанным ключом, и hv_fetch вернёт результат, как если бы значение уже существовало.
Помните, что hv_store и hv_fetch возвращают SV** и не просто SV*. Для доступа к значению скаляра необходимо сначала разыменовать возвращаемое значение. Однако перед разыменованием необходимо убедиться, что возвращаемое значение не равно NULL.
Первая из этих двух функций проверяет, существует ли запись в таблице хеширования, а вторая удаляет её.
bool hv_exists(HV*, const char* key, U32 klen);
SV* hv_delete(HV*, const char* key, U32 klen, I32 flags); Если flags не включает флаг G_DISCARD, то hv_delete создаст и вернёт смертную копию удалённого значения.
И несколько дополнительных функций:
void hv_clear(HV*);
void hv_undef(HV*); Как и их аналоги для AV, hv_clear удаляет все записи в таблице хеширования, но не удаляет саму таблицу. hv_undef удаляет все записи и саму таблицу.
Perl хранит фактические данные в связанном списке структур с типом данных HE. Они содержат фактические указатели на ключ и значение (плюс дополнительные административные данные). Ключ — указатель на строку; значение — SV*. Однако после получения HE*, чтобы получить фактический ключ и значение, используйте указанные ниже процедуры.
I32 hv_iterinit(HV*);
/* Prepares starting point to traverse hash table */
HE* hv_iternext(HV*);
/* Get the next entry, and return a pointer to a
structure that has both the key and value */
char* hv_iterkey(HE* entry, I32* retlen);
/* Get the key from an HE structure and also return
the length of the key string */
SV* hv_iterval(HV*, HE* entry);
/* Return an SV pointer to the value of the HE
structure */
SV* hv_iternextsv(HV*, char** key, I32* retlen);
/* This convenience routine combines hv_iternext,
hv_iterkey, and hv_iterval. The key and retlen
arguments are return values for the key and its
length. The value is returned in the SV* argument */ Если вам известна переменная хеша, вы можете получить указатель на её HV, используя следующее:
HV* get_hv("package::varname", 0); Это возвращает NULL, если переменная не существует.
Алгоритм хеширования определён в макросе PERL_HASH.
PERL_HASH(hash, key, klen) Точное выполнение этого макроса зависит от архитектуры и версии Perl, и возвращаемое значение может изменяться при каждом вызове, поэтому значение действительно только в течение одного процесса Perl.
См. "Понимание магии привязанных хешей и массивов" для получения дополнительной информации о том, как использовать функции доступа к хешам в привязанных хешах.
Расширения API хешей
Начиная с версии 5.004, также поддерживаются следующие функции:
HE* hv_fetch_ent (HV* tb, SV* key, I32 lval, U32 hash);
HE* hv_store_ent (HV* tb, SV* key, SV* val, U32 hash);
bool hv_exists_ent (HV* tb, SV* key, U32 hash);
SV* hv_delete_ent (HV* tb, SV* key, I32 flags, U32 hash);
SV* hv_iterkeysv (HE* entry); Обратите внимание, что эти функции принимают ключи SV*, что упрощает написание кода расширения, работающего со структурами хешей. Эти функции также позволяют передавать ключи SV* функциям tie без принудительного преобразования ключей в строки (в отличие от предыдущего набора функций).
Они также возвращают и принимают целые записи хешей (HE*), что делает их использование более эффективным (поскольку номер хеша для конкретной строки не нужно вычислять каждый раз). Подробные описания см. в perlapi.
Для доступа к содержимому записей хешей всегда необходимо использовать следующие макросы. Обратите внимание, что аргументы этих макросов должны быть простыми переменными, так как они могут быть вычислены более одного раза. Подробные описания этих макросов см. в perlapi.
HePV(HE* he, STRLEN len)
HeVAL(HE* he)
HeHASH(HE* he)
HeSVKEY(HE* he)
HeSVKEY_force(HE* he)
HeSVKEY_set(HE* he, SV* sv) Эти два макроса низкого уровня определены, но должны использоваться только при работе с ключами, которые не являются SV*:
HeKEY(HE* he)
HeKLEN(HE* he) Обратите внимание, что как hv_store, так и hv_store_ent не увеличивают счетчик ссылок хранимого val, что является обязанностью вызывающей стороны. Если эти функции возвращают значение NULL, вызывающей стороне, как правило, необходимо уменьшить счетчик ссылок val для предотвращения утечки памяти.
AVs, HVs и неопределённые значения
Иногда необходимо хранить неопределённые значения в AVs или HVs. Хотя это может быть редкий случай, он может быть сложным. Это связано с тем, что вы привыкли использовать &PL_sv_undef если вам нужен неопределённый SV.
Например, интуиция подсказывает, что этот код XS:
AV *av = newAV();
av_store( av, 0, &PL_sv_undef ); эквивалентен этому коду Perl:
my @av;
$av[0] = undef; К сожалению, это неверно. В Perl 5.18 и более ранних версиях AVs используют &PL_sv_undef в качестве маркера, указывающего, что элемент массива ещё не был инициализирован. Таким образом, exists $av[0] будет истинным для приведенного выше кода Perl, но ложным для массива, сгенерированного кодом XS. В Perl 5.20 хранение &PL_sv_undef создаст элемент только для чтения, поскольку хранится сам скаляр &PL_sv_undef, а не его копия.
Аналогичные проблемы могут возникнуть при хранении &PL_sv_undef в HVs:
hv_store( hv, "key", 3, &PL_sv_undef, 0 ); Это действительно сделает значение undef, но если вы попытаетесь изменить значение key, вы получите следующую ошибку:
Modification of non-creatable hash value attempted В Perl 5.8.0 &PL_sv_undef также использовался для обозначения заглушек в ограниченных хешах. Это приводило к тому, что такие записи хеша не отображались при итерации по хешу или при проверке ключей с помощью функции hv_exists.
Вы можете столкнуться с аналогичными проблемами при хранении &PL_sv_yes или &PL_sv_no в AVs или HVs. Попытка изменить такие элементы выдаст следующую ошибку:
Modification of a read-only value attempted Короче говоря, вы можете использовать специальные переменные &PL_sv_undef, &PL_sv_yes и &PL_sv_no с AVs и HVs, но вы должны убедиться, что знаете, что делаете.
В общем случае, если вы хотите хранить неопределённое значение в 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 указывает, к какому классу будет принадлежать ссылка. См. "Хранилища и глобальные переменные" для получения информации о преобразовании имён классов в хранилища.
/* Работа всё ещё ведётся */
Следующая функция повышает rv до ссылки, если она ею не является. Создаёт новый SV для ссылки rv. Если classname не равно NULL, SV благословляется в указанный класс. Возвращается SV.
SV* newSVrv(SV* rv, const char* classname); Следующие три функции копируют целое, беззнаковое целое или двойное значение в SV, ссылка на который — rv. SV благословляется, если classname не равно NULL.
SV* sv_setref_iv(SV* rv, const char* classname, IV iv);
SV* sv_setref_uv(SV* rv, const char* classname, UV uv);
SV* sv_setref_nv(SV* rv, const char* classname, NV iv); Следующая функция копирует значение указателя (адрес, а не строка!) в SV, ссылка на который — rv. SV благословляется, если classname не равно NULL.
SV* sv_setref_pv(SV* rv, const char* classname, void* pv); Следующая функция копирует строку в SV, ссылка на который — rv. Установите длину в 0, чтобы позволить Perl вычислить длину строки. SV благословляется, если classname не равно NULL.
SV* sv_setref_pvn(SV* rv, const char* classname, char* pv,
STRLEN length); Следующая функция проверяет, благословен ли SV в указанный класс. Она не проверяет отношения наследования.
int sv_isa(SV* sv, const char* name); Следующая функция проверяет, является ли SV ссылкой на благословлённый объект.
int sv_isobject(SV* sv); Следующая функция проверяет, происходит ли SV от указанного класса. SV может быть либо ссылкой на благословлённый объект, либо строкой, содержащей имя класса. Это функция, реализующая функциональность UNIVERSAL::isa.
bool sv_derived_from(SV* sv, const char* name); Чтобы проверить, имеете ли вы объект, производный от определённого класса, нужно написать:
if (sv_isobject(sv) && sv_derived_from(sv, class)) { ... } Создание новых переменных
Чтобы создать новую переменную Perl с неопределённым значением, доступной из вашего скрипта 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)); Функции для смертных объектов не ограничиваются SVs; 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.
Дескрипторы ввода/вывода
Как и AV и HV, объекты IO — это другой тип нескалярного SV, который может содержать объекты PerlIO для ввода и вывода или DIR * из opendir().
Вы можете создать новый объект IO:
IO* newIO(); В отличие от других SV, новый объект IO автоматически благословляется в класс IO::File.
Объект IO содержит дескриптор PerlIO для ввода и вывода:
PerlIO *IoIFP(IO *io);
PerlIO *IoOFP(IO *io); Как правило, если объект IO был открыт для файла, дескриптор ввода всегда присутствует, но дескриптор вывода присутствует только в том случае, если файл открыт для вывода. Для файла, если оба присутствуют, они будут одним и тем же объектом PerlIO.
Различные объекты PerlIO для ввода и вывода создаются для сокетов и символьных устройств.
Объект IO также содержит другие данные, связанные с дескрипторами Perl I/O:
IV IoLINES(io); /* $. */
IV IoPAGE(io); /* $% */
IV IoPAGE_LEN(io); /* $= */
IV IoLINES_LEFT(io); /* $- */
char *IoTOP_NAME(io); /* $^ */
GV *IoTOP_GV(io); /* $^ */
char *IoFMT_NAME(io); /* $~ */
GV *IoFMT_GV(io); /* $~ */
char *IoBOTTOM_NAME(io);
GV *IoBOTTOM_GV(io);
char IoTYPE(io);
U8 IoFLAGS(io);
=for apidoc_sections $io_scn, $formats_section
=for apidoc_section $reports
=for apidoc Amh|IV|IoLINES|IO *io
=for apidoc Amh|IV|IoPAGE|IO *io
=for apidoc Amh|IV|IoPAGE_LEN|IO *io
=for apidoc Amh|IV|IoLINES_LEFT|IO *io
=for apidoc Amh|char *|IoTOP_NAME|IO *io
=for apidoc Amh|GV *|IoTOP_GV|IO *io
=for apidoc Amh|char *|IoFMT_NAME|IO *io
=for apidoc Amh|GV *|IoFMT_GV|IO *io
=for apidoc Amh|char *|IoBOTTOM_NAME|IO *io
=for apidoc Amh|GV *|IoBOTTOM_GV|IO *io
=for apidoc_section $io
=for apidoc Amh|char|IoTYPE|IO *io
=for apidoc Amh|U8|IoFLAGS|IO *io Большинство из них связано с форматами.
IoFLAGs() может содержать сочетание флагов, наиболее интересными из которых являются IOf_FLUSH ($|) для автоматического сброса и IOf_UNTAINT, устанавливаемого с помощью метода IO::Handle's untaint().
Объект IO также может содержать дескриптор каталога:
DIR *IoDIRP(io); пригодный для использования с PerlDir_read() и т. д.
Все эти макросы-аксессоры являются lvalue, нет отдельных макросов _set() для изменения членов объекта IO.
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, и т. д., вызовут ошибку «Модификация значения только для чтения» в этих версиях, — это:
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 не равно нулю, то либо копия name (если namlen больше нуля), либо само name (если namlen равно нулю) хранится в поле mg_ptr. В качестве специального случая, если (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_regdata, или является указателем NULL, то obj просто сохраняется без увеличения счётчика ссылок.
См. также sv_magicext в perlapi для более гибкого способа добавления магии к SV.
Также есть функция для добавления магии к HV:
Это просто вызывает sv_magic и преобразует аргумент gv в SV.
Для удаления магии из SV вызовите функцию sv_unmagic:
int sv_unmagic(SV *sv, int type);Аргумент type должен быть равен значению how, когда SV первоначально был сделан магическим.
Однако обратите внимание, что sv_unmagic удаляет всю магию определённого типа type из SV. Если вы хотите удалить только определённую магию type на основе виртуальной таблицы магии, используйте sv_unmagicext вместо этого:
Магические виртуальные таблицы
Поле mg_virtual в структуре MAGIC является указателем на MGVTBL, что является структурой указателей на функции, представляющей "Магическую виртуальную таблицу" для обработки различных операций, которые могут быть применены к этой переменной.
Структура MGVTBL содержит пять (или иногда восемь) указателей на следующие типы процедур:
int (*svt_get) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_set) (pTHX_ SV* sv, MAGIC* mg);
U32 (*svt_len) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_clear)(pTHX_ SV* sv, MAGIC* mg);
int (*svt_free) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_copy) (pTHX_ SV *sv, MAGIC* mg, SV *nsv,
const char *name, I32 namlen);
int (*svt_dup) (pTHX_ MAGIC *mg, CLONE_PARAMS *param);
int (*svt_local)(pTHX_ SV *nsv, MAGIC *mg); Структура MGVTBL устанавливается во время компиляции в файле perl.h и в настоящее время насчитывает 32 типа. Эти различные структуры содержат указатели на различные процедуры, которые выполняют дополнительные действия в зависимости от вызываемой функции.
Function pointer Action taken
---------------- ------------
svt_get Do something before the value of the SV is
retrieved.
svt_set Do something after the SV is assigned a value.
svt_len Report on the SV's length.
svt_clear Clear something the SV represents.
svt_free Free any extra storage associated with the SV.
svt_copy copy tied variable magic to a tied element
svt_dup duplicate a magic structure during thread cloning
svt_local copy magic to local value during 'local' Например, структура MGVTBL под названием vtbl_sv (которая соответствует типу mg_type PERL_MAGIC_sv) содержит:
{ magic_get, magic_set, magic_len, 0, 0 } Таким образом, когда SV определяется как магический и типа PERL_MAGIC_sv, если выполняется операция получения, вызывается процедура magic_get. Все различные процедуры для различных типов магии начинаются с magic_. ПРИМЕЧАНИЕ: процедуры магии не считаются частью Perl API и могут не экспортироваться Perl библиотекой.
Последние три слота являются недавним дополнением, и для совместимости исходного кода они проверяются только в том случае, если один из трёх флагов MGf_COPY, MGf_DUP, или MGf_LOCAL установлен в mg_flags. Это означает, что большая часть кода может продолжать объявлять vtable как значение из пяти элементов. В настоящее время эти три флага используются исключительно кодом потоков и могут быть изменены.
Текущие виды магических виртуальных таблиц:
mg_type
(old-style char and macro) MGVTBL Type of magic
-------------------------- ------ -------------
\0 PERL_MAGIC_sv vtbl_sv Special scalar variable
# PERL_MAGIC_arylen vtbl_arylen Array length ($#ary)
% PERL_MAGIC_rhash (none) Extra data for restricted
hashes
* PERL_MAGIC_debugvar vtbl_debugvar $DB::single, signal, trace
vars
. PERL_MAGIC_pos vtbl_pos pos() lvalue
: PERL_MAGIC_symtab (none) Extra data for symbol
tables
< PERL_MAGIC_backref vtbl_backref For weak ref data
@ PERL_MAGIC_arylen_p (none) To move arylen out of XPVAV
B PERL_MAGIC_bm vtbl_regexp Boyer-Moore
(fast string search)
c PERL_MAGIC_overload_table vtbl_ovrld Holds overload table
(AMT) on stash
D PERL_MAGIC_regdata vtbl_regdata Regex match position data
(@+ and @- vars)
d PERL_MAGIC_regdatum vtbl_regdatum Regex match position data
element
E PERL_MAGIC_env vtbl_env %ENV hash
e PERL_MAGIC_envelem vtbl_envelem %ENV hash element
f PERL_MAGIC_fm vtbl_regexp Formline
('compiled' format)
g PERL_MAGIC_regex_global vtbl_mglob m//g target
H PERL_MAGIC_hints vtbl_hints %^H hash
h PERL_MAGIC_hintselem vtbl_hintselem %^H hash element
I PERL_MAGIC_isa vtbl_isa @ISA array
i PERL_MAGIC_isaelem vtbl_isaelem @ISA array element
k PERL_MAGIC_nkeys vtbl_nkeys scalar(keys()) lvalue
L PERL_MAGIC_dbfile (none) Debugger %_<filename
l PERL_MAGIC_dbline vtbl_dbline Debugger %_<filename
element
N PERL_MAGIC_shared (none) Shared between threads
n PERL_MAGIC_shared_scalar (none) Shared between threads
o PERL_MAGIC_collxfrm vtbl_collxfrm Locale transformation
P PERL_MAGIC_tied vtbl_pack Tied array or hash
p PERL_MAGIC_tiedelem vtbl_packelem Tied array or hash element
q PERL_MAGIC_tiedscalar vtbl_packelem Tied scalar or handle
r PERL_MAGIC_qr vtbl_regexp Precompiled qr// regex
S PERL_MAGIC_sig vtbl_sig %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_destruct vtbl_destruct destruct callback
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
Z PERL_MAGIC_hook vtbl_hook %{^HOOK} hash
z PERL_MAGIC_hookelem vtbl_hookelem %{^HOOK} hash element
\ PERL_MAGIC_lvref vtbl_lvref Lvalue reference
constructor
] PERL_MAGIC_checkcall vtbl_checkcall Inlining/mutation of call
to this CV
^ PERL_MAGIC_extvalue (none) Value magic available for
use by extensions
~ PERL_MAGIC_ext (none) Variable magic available
for use by extensions Когда в таблице присутствуют как прописная, так и строчная буквы, заглавная буква обычно используется для представления некоторого составного типа (список или хеш), а строчная буква - для представления элемента этого составного типа. Некоторые внутренние части кода используют это соответствие регистров. Однако 'v' и 'V' (vec и v-строка) никак не связаны.
Магические типы PERL_MAGIC_ext, PERL_MAGIC_extvalue и PERL_MAGIC_uvar определены специально для использования расширениями и не будут использоваться самим Perl. Расширения могут использовать магию PERL_MAGIC_ext или PERL_MAGIC_extvalue для «прикрепления» частной информации к переменным (обычно к объектам). Это особенно полезно, потому что обычный perl-код не может повредить эту частную информацию (в отличие от использования дополнительных элементов объекта хеша). PERL_MAGIC_extvalue представляет магию значения (в отличие от PERL_MAGIC_ext и PERL_MAGIC_uvar), что означает, что при локализации новое значение не будет магическим.
Аналогично, магия PERL_MAGIC_uvar может использоваться очень похоже на tie(), для вызова C-функции всякий раз, когда значение скаляра используется или изменяется. Поле mg_ptr в MAGIC указывает на структуру ufuncs:
struct ufuncs {
I32 (*uf_val)(pTHX_ IV, SV*);
I32 (*uf_set)(pTHX_ IV, SV*);
IV uf_index;
}; При чтении или записи SV вызывается функция uf_val или uf_set с uf_index в качестве первого аргумента и указателем на SV во втором. Пример простого добавления магии PERL_MAGIC_uvar показан ниже. Обратите внимание, что структура ufuncs копируется функцией sv_magic, поэтому вы можете безопасно выделить её в стеке.
void
Umagic(sv)
SV *sv;
PREINIT:
struct ufuncs uf;
CODE:
uf.uf_val = &my_get_fn;
uf.uf_set = &my_set_fn;
uf.uf_index = 0;
sv_magic(sv, 0, PERL_MAGIC_uvar, (char*)&uf, sizeof(uf)); Прикрепление PERL_MAGIC_uvar к массивам разрешено, но не оказывает никакого влияния.
Для хешей есть специализированный крючок, который даёт контроль над ключами хеша (но не значениями). Этот крючок вызывает магию 'get' PERL_MAGIC_uvar если функция «set» в структуре ufuncs равна NULL. Крючок активируется всякий раз, когда к хешу обращаются с ключом, указанным как SV через функции hv_store_ent, hv_fetch_ent, hv_delete_ent и hv_exists_ent. Обращение к ключу как к строке через функции без суффикса ..._ent обходит крючок. См. "GUTS" в Hash::Util::FieldHash для подробного описания.
Поскольку несколько расширений могут использовать магию PERL_MAGIC_ext или PERL_MAGIC_uvar, расширениям важно проявлять особую осторожность, чтобы избежать конфликтов. Обычно достаточно использовать магию только для объектов, благословлённых тем же классом, что и расширение. Для магии PERL_MAGIC_ext обычно рекомендуется определить MGVTBL, даже если все её поля будут 0, чтобы отдельные указатели MAGIC можно было идентифицировать как определённый тип магии с помощью их магической виртуальной таблицы. mg_findext предоставляет лёгкий способ сделать это:
STATIC MGVTBL my_vtbl = { 0, 0, 0, 0, 0, 0, 0, 0 };
MAGIC *mg;
if ((mg = mg_findext(sv, PERL_MAGIC_ext, &my_vtbl))) {
/* this is really ours, not another module's PERL_MAGIC_ext */
my_priv_data_t *priv = (my_priv_data_t *)mg->mg_ptr;
...
} Также обратите внимание, что функции sv_set*() и sv_cat*(), описанные ранее, не вызывают магию 'set' на их целях. Это должно быть сделано пользователем, либо вызвав макрос SvSETMAGIC() после вызова этих функций, либо используя одну из функций sv_set*_mg() или sv_cat*_mg(). Аналогично, общий C-код должен вызвать макрос SvGETMAGIC() для вызова магии 'get', если они используют SV, полученный из внешних источников, в функциях, которые не обрабатывают магию. См. perlapi для описания этих функций. Например, вызовы функций sv_cat*() обычно требуют последующего вызова SvSETMAGIC(), но им не нужен предварительный SvGETMAGIC(), так как их реализация обрабатывает магию 'get'.
Поиск магии
MAGIC *mg_find(SV *sv, int type); /* Finds the magic pointer of that
* type */ Эта функция возвращает указатель на структуру MAGIC, хранящуюся в SV. Если в SV нет указанного магического свойства, возвращается NULL. Если в SV есть несколько экземпляров этого магического свойства, будет возвращён первый.
mg_findext может использоваться для поиска структуры MAGIC SV по типу магии и её виртуальной таблице:
MAGIC *mg_findext(SV *sv, int type, MGVTBL *vtbl); Также, если SV, переданный в mg_find или mg_findext, не имеет тип SVt_PVMG, Perl может аварийно завершить работу.
int mg_copy(SV* sv, SV* nsv, const char* key, STRLEN klen); Эта функция проверяет типы магии, которые имеет sv. Если поле mg_type - заглавная буква, то mg_obj копируется в nsv, но поле mg_type изменяется на строчную букву.
Понимание магии привязанных хешей и массивов
Привязанные хеши и массивы являются магическими сущностями типа магии PERL_MAGIC_tied.
ПРЕДУПРЕЖДЕНИЕ: Начиная с версии 5.004, для правильного использования функций доступа к массивам и хешам необходимо учитывать несколько оговорок. Некоторые из этих оговорок на самом деле считаются ошибками API, которые будут исправлены в последующих выпусках, и помечены меткой [MAYCHANGE] ниже. Если вы обнаружите себя применяющим информацию из этого раздела, помните, что поведение может измениться в будущем, эмм, без предупреждения.
Функция perl tie связывает переменную с объектом, реализующим различные методы GET, SET и т. д. Для имитации функции perl tie из XSUB необходимо повторить это поведение. Ниже представлен код, выполняющий необходимые шаги - сначала создаётся новый хеш, затем второй хеш, который благословляется в класс, реализующий методы 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 может быть изменено для обеспечения более прозрачного доступа к связанным и обычным типам данных.
Вам будет полезно понять, что интерфейсы TIEARRAY и TIEHASH — всего лишь синтаксический сахар для вызова некоторых вызовов методов Perl при использовании унифицированного синтаксиса хэшей и массивов. Использование этого синтаксического сахара создаёт некоторую нагрузку (обычно около двух-четырёх дополнительных инструкций за операцию FETCH/STORE, помимо создания всех необходимых переменных для вызова методов). Эта нагрузка будет сравнительно небольшой, если сами методы TIE значительны, но если они состоят всего из нескольких операторов, нагрузка не будет незначительной.
Локализация изменений
Perl имеет очень удобную конструкцию
{
local $var = 2;
...
} Эта конструкция приблизительно эквивалентна
{
my $oldvar = $var;
$var = 2;
...
$var = $oldvar;
} Самое большое различие заключается в том, что первая конструкция восстановит начальное значение $var независимо от того, как управление выходит из блока: goto, return, die/eval, и т.д. Она также немного эффективнее.
Существует способ достижения аналогичной задачи из C через API Perl: создать псевдоблок и организовать автоматическое отмену некоторых изменений в конце его, либо явно, либо через нелокальный выход (через die()). Блочная конструкция создаётся парой макросов ENTER/LEAVE (см. "Возврат скаляра" в perlcall). Такая конструкция может быть создана специально для какой-либо важной задачи локализации или может быть использована уже существующая (например, границы охватывающей подпрограммы Perl/блока или уже существующая пара для освобождения TMP). (Во втором случае накладные расходы дополнительной локализации должны быть почти незначительными.) Обратите внимание, что любой XSUB автоматически заключён в пару ENTER/LEAVE.
Внутри такого псевдоблока доступна следующая служба:
-
SAVEINT(int i) -
SAVEIV(IV i) -
SAVEI32(I32 i) -
SAVELONG(long i) -
SAVEI8(I8 i) -
SAVEI16(I16 i) -
SAVEBOOL(int i) -
SAVESTRLEN(STRLEN i) -
Эти макросы обеспечивают восстановление значения целочисленной переменной
iв конце охватывающего псевдоблока. -
SAVESPTR(s) -
SAVEPPTR(p) -
Эти макросы обеспечивают восстановление значений указателей
sиp.sдолжен быть указателем типа, который сохраняется при преобразовании вSV*и обратно,pдолжен быть способен сохраниться при преобразовании вchar*и обратно. -
SAVERCPV(char **ppv) -
Этот макрос обеспечивает восстановление значения переменной типа
char *, которая была выделена с помощью вызоваrcpv_new(), до предыдущего состояния при завершении текущего псевдоблока. Указатель, хранящийся в*ppvв момент вызова, будет инкрементирован на счетчик ссылок и сохранён в стеке сохранения. Позже, когда текущий псевдоблок будет завершён, значение, хранящееся в*ppv, будет декрементировано на счетчик ссылок, и предыдущее значение будет восстановлено из стека сохранения, который также будет декрементирован на счетчик ссылок.Это аналог
RCPVвSAVEGENERICSV(). -
SAVEGENERICSV(SV **psv) -
Этот макрос обеспечивает восстановление значения переменной типа
SV *до предыдущего состояния при завершении текущего псевдоблока. Указатель, хранящийся в*psvв момент вызова, будет инкрементирован на счетчик ссылок и сохранён в стеке сохранения. Позже, когда текущий псевдоблок будет завершён, значение, хранящееся в*ppv, будет декрементировано на счетчик ссылок, и предыдущее значение будет восстановлено из стека сохранения, который также будет декрементирован на счетчик ссылок. Это аналогlocal $svв C. -
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, освобождается в конце текущего псевдоблока. -
SAVEFREERCPV(char *pv) -
Обеспечивает освобождение
char *, созданного вызовомrcpv_new(), в конце текущего псевдоблока.Это эквивалент
SAVEFREESV()для RCPV. -
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, который может быть NULL. -
SAVEDESTRUCTOR_X(DESTRUCTORFUNC_t f, void *p) -
В конце псевдоблока вызывается функция
fс неявным аргументом контекста (если таковой имеется) иp, который может быть NULL.Обратите внимание, что конец текущего псевдоблока может наступить значительно позже, чем конец текущего оператора. Возможно, вам следует рассмотреть макрос
MORTALDESTRUCTOR_X(). -
MORTALSVFUNC_X(SVFUNC_t f, SV *sv) -
В конце текущего оператора вызывается функция
fс неявным аргументом контекста (если таковой имеется) иsv, который может быть NULL.Обратите внимание, что аргумент параметра функции-деструктора отличается от связанного
SAVEDESTRUCTOR_X()тем, что он ДОЛЖЕН быть либо NULL, либоSV*.Обратите внимание, что конец текущего оператора может наступить значительно раньше, чем конец текущего псевдоблока. Возможно, вам следует рассмотреть макрос
SAVEDESTRUCTOR_X()вместо этого. -
MORTALDESTRUCTOR_SV(SV *coderef, SV *args) -
В конце текущего оператора вызывается Perl-функция, содержащаяся в
coderef, с предоставленными (при наличии) аргументами вargs. См. документацию дляmortal_destructor_sv()для получения подробностей о том, как обрабатывается параметрargs.Обратите внимание, что конец текущего оператора может наступить значительно раньше, чем конец текущего псевдоблока. Если вы хотите вызвать Perl-функцию в конце текущего псевдоблока, вам следует использовать API
SAVEDESTRUCTOR_X(), для чего вам потребуется создать C-обёртку для вызова Perl-функции. -
SAVESTACK_POS() -
Текущее смещение в внутреннем стеке Perl (см.
SP) восстанавливается в конце псевдоблока.
Следующий список API содержит функции, поэтому необходимо явно указать указатели на изменяемые данные (либо C-указатели, либо Perl'ские GV *). Где вышеупомянутые макросы принимают int, аналогичная функция принимает int *.
Другие макросы выше имеют реализующие их функции, но, вероятно, лучше просто использовать макрос, а не их или нижеследующие.
-
SV* save_scalar(GV *gv) -
Эквивалент Perl-коду
local $gv. -
AV* save_ary(GV *gv) -
HV* save_hash(GV *gv) -
Аналогично
save_scalar, но локализует@gvи%gv. -
void save_item(SV *item) -
Дублирует текущее значение
SV. При выходе из текущего псевдоблокаENTER/LEAVEзначениеSVбудет восстановлено с помощью сохранённого значения. Он не обрабатывает магию. Используйтеsave_scalar, если магия затронута. -
SV* save_svref(SV **sptr) -
Аналогично
save_scalar, но восстановитSV *. -
void save_aptr(AV **aptr) -
void save_hptr(HV **hptr) -
Аналогично
save_svref, но локализуетAV *иHV *.
Модуль Alias реализует локализацию базовых типов внутри области видимости вызывающей функции. Те, кто интересуется, как локализовать вещи в содержащей области видимости, тоже должны посмотреть туда.
Подпрограммы
XSUB и стек аргументов
Механизм XSUB — простой способ для программ Perl доступа к C-подпрограммам. Подпрограмма XSUB будет иметь стек, содержащий аргументы из программы Perl, и способ сопоставления Perl-структур данных с C-эквивалентами.
Аргументы стека доступны через макрос ST(n), который возвращает n-й аргумент стека. Аргумент 0 — первый аргумент, переданный в вызов Perl-подпрограммы. Эти аргументы SV*, и их можно использовать везде, где используется SV*.
Большую часть времени вывод из C-функции можно обрабатывать с помощью директив RETVAL и OUTPUT. Однако есть случаи, когда стек аргументов не достаточно велик для обработки всех возвращаемых значений. Примером является вызов POSIX tzname(), который не принимает аргументы, но возвращает два: стандартное и летнее время зоны.
Для обработки этой ситуации используется директива PPCODE, а стек расширяется с помощью макроса:
EXTEND(SP, num); где SP — макрос, представляющий локальную копию указателя стека, а num — количество элементов, на которое нужно расширить стек.
Теперь, когда на стеке есть место, значения можно поместить на него с помощью макроса PUSHs. Помещенные значения часто должны быть «смертными» (см. "Счётчики ссылок и смертность"):
PUSHs(sv_2mortal(newSViv(an_integer)))
PUSHs(sv_2mortal(newSVuv(an_unsigned_integer)))
PUSHs(sv_2mortal(newSVnv(a_double)))
PUSHs(sv_2mortal(newSVpv("Some String",0)))
/* Although the last example is better written as the more
* efficient: */
PUSHs(newSVpvs_flags("Some String", SVs_TEMP)) И теперь программа Perl, вызывающая tzname, присвоит два значения так:
($standard_abbrev, $summer_abbrev) = POSIX::tzname; Альтернативный (и возможно более простой) метод помещения значений на стек — использование макроса:
XPUSHs(SV*) Этот макрос автоматически корректирует стек при необходимости. Таким образом, вам не нужно вызывать EXTEND для расширения стека.
Несмотря на заявления в предыдущих версиях этого документа, макросы (X)PUSH[iunp] не подходят для XSUB, возвращающих несколько результатов. В этом случае придерживайтесь макросов (X)PUSHs выше или используйте новые макросы m(X)PUSH[iunp]; см. "Помещение значения C в стек Perl".
Для получения дополнительной информации см. perlxs и perlxstut.
Автозагрузка с XSUB
Если функция AUTOLOAD — это XSUB, как и Perl-подпрограммы, Perl помещает полное квалифицированное имя автозагружаемой подпрограммы в переменную $AUTOLOAD пакета XSUB.
Но также он помещает эту информацию в определённые поля самого XSUB:
HV *stash = CvSTASH(cv);
const char *subname = SvPVX(cv);
STRLEN name_length = SvCUR(cv); /* in bytes */
U32 is_utf8 = SvUTF8(cv); SvPVX(cv) содержит только само имя подпрограммы, без пакета. Для функции AUTOLOAD в UNIVERSAL или одном из его суперклассов CvSTASH(cv) возвращает NULL во время вызова метода несуществующего пакета.
Примечание: Настройка $AUTOLOAD перестала работать в 5.6.1, которая вообще не поддерживала XS AUTOLOAD подпрограммы. Perl 5.8.0 ввёл использование полей в самом XSUB. Perl 5.16.0 вернул настройку $AUTOLOAD. Если вам нужно поддерживать 5.8-5.14, используйте поля XSUB.
Вызов Perl-подпрограмм из C-программ
Есть четыре подпрограммы, которые можно использовать для вызова Perl-подпрограммы из C-программы:
I32 call_sv(SV*, I32);
I32 call_pv(const char*, I32);
I32 call_method(const char*, I32);
I32 call_argv(const char*, I32, char**); Наиболее часто используемая подпрограмма — call_sv. Аргумент SV* содержит либо имя вызываемой Perl-подпрограммы, либо ссылку на неё. Второй аргумент состоит из флагов, которые управляют контекстом вызова подпрограммы, передаются ли подпрограмме аргументы, как следует обрабатывать ошибки и как следует обрабатывать возвращаемые значения.
Все четыре подпрограммы возвращают количество аргументов, возвращённых подпрограммой в стеке Perl.
Эти подпрограммы раньше назывались perl_call_sv, и т. д., до Perl v5.6.0, но теперь эти имена устарели; макросы с такими же именами предоставляются для совместимости.
При использовании любой из этих подпрограмм (кроме call_argv) программист должен манипулировать стеком Perl. Это включает в себя следующие макросы и функции:
dSP
SP
PUSHMARK()
PUTBACK
SPAGAIN
ENTER
SAVETMPS
FREETMPS
LEAVE
XPUSH*()
POP*() Для подробного описания правил вызова из C в Perl см. perlcall.
Помещение значения C в стек Perl
Многие коды операций (это элементарная операция в внутренней машине стека Perl) помещают SV* в стек. Однако в качестве оптимизации соответствующий SV (обычно) не создаётся каждый раз. Коды операций повторно используют специально назначенные SV (targetы), которые (как следствие) не постоянно освобождаются/создаются.
Каждый из target'ов создаётся только один раз (но см. "Схемы и рекурсия" ниже), и когда коду операции нужно поместить целое число, двойное число или строку в стек, он просто устанавливает соответствующие части своего target и помещает target в стек.
Макрос для помещения этого целевого элемента в стек — 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, которые являются targetами для кодов операций. Ответ заключается в том, что они создаются при компиляции текущего блока — подпрограммы или файла (для кодов операций вне подпрограмм). В это время создается специальный анонимный массив Perl, который называется схемой для текущего блока.
Схема хранит SV, которые являются лексическими для текущего блока и являются целевыми для кодов операций. В предыдущей версии этого документа говорилось, что можно определить, что SV находится в схеме, посмотрев на его флаги: лексические переменные имеют SVs_PADMY установлен, а targetы имеют SVs_PADTMP установлен. Но это никогда не было полностью правдой. SVs_PADMY может быть установлен на переменной, которая больше не находится в любом блоке. В то время как targetы имеют SVs_PADTMP установлен, он также может быть установлен на переменных, которые никогда не находились в блоке, но тем не менее ведут себя как targetы. С версии Perl 5.21.5 флаг SVs_PADMY больше не используется и определён как 0. SvPADMY() теперь возвращает true для всего, у чего нет SVs_PADTMP.
Соответствие между OP и targetами не является 1 к 1. Различные OP в дереве компиляции блока могут использовать один и тот же target, если это не противоречит ожидаемой продолжительности существования временной переменной.
Схемы и рекурсия
На самом деле не совсем верно, что скомпилированный блок содержит указатель на массив схемы AV. На самом деле он содержит указатель на массив AV из (изначально) одного элемента, а этот элемент — массив схемы AV. Зачем нам нужен дополнительный уровень косвенности?
Ответ — рекурсия и возможно потоки. Оба этих варианта могут создавать несколько указателей выполнения, переходящих в одну и ту же подпрограмму. Чтобы дочерняя подпрограмма не перезаписывала временные переменные родительской подпрограммы (жизненный цикл которой охватывает вызов дочерней), родительская и дочерняя подпрограммы должны иметь разные схемы. (И лексические переменные должны быть отдельными!).
Итак, каждая подпрограмма рождается с массивом схем (длиной 1). При каждом входе в подпрограмму проверяется, не превышает ли текущая глубина рекурсии длину этого массива, и если превышает, создаётся новая схема и добавляется в массив.
Targetы в этой схеме — undefs, но они уже помечены правильными флагами.
Выделение памяти
Выделение
Вся память, предназначенная для использования с функциями Perl API, должна обрабатываться с помощью макросов, описанных в этом разделе. Макросы обеспечивают необходимую прозрачность между различиями в фактической реализации malloc, используемой в Perl.
Для начального выделения памяти используются следующие три макроса:
Newx(pointer, number, type);
Newxc(pointer, number, type, cast);
Newxz(pointer, number, type); Первый аргумент pointer должен быть именем переменной, которая будет указывать на только что выделенную память.
Второй и третий аргументы number и type указывают, сколько структур заданного типа данных нужно выделить. Аргумент type передаётся в sizeof. Конечный аргумент для Newxc, cast, следует использовать, если аргумент pointer отличается от аргумента type.
В отличие от макросов Newx и Newxc, макрос Newxz вызывает memzero для обнуления всей только что выделенной памяти.
Перевыделение
Renew(pointer, number, type);
Renewc(pointer, number, type, cast);
Safefree(pointer) Эти три макроса используются для изменения размера буфера памяти или для освобождения больше не нужной части памяти. Аргументы для Renew и Renewc соответствуют аргументам New и Newc, за исключением того, что не требуется аргумент «магическая метка».
Перемещение
Move(source, dest, number, type);
Copy(source, dest, number, type);
Zero(dest, number, type); Эти три макроса используются для перемещения, копирования или обнуления ранее выделенной памяти. Аргументы source и dest указывают на начальные точки источника и назначения. Perl переместит, скопирует или обнулит number экземпляров размера структуры данных type (используя функцию sizeof).
PerlIO
В последних выпусках Perl проводились эксперименты по удалению зависимости Perl от стандартного набора ввода-вывода и разрешению использования других реализаций stdio. Это включает в себя создание нового уровня абстракции, который затем вызывает ту реализацию stdio, с которой был скомпилирован Perl. Все XSUB теперь должны использовать функции на уровне абстракции PerlIO и не должны делать никаких предположений о том, какой тип stdio используется.
Для полного описания абстракции PerlIO см. perlapio.
Скомпилированный код
Дерево кода
Здесь мы описываем внутреннюю форму, в которую Perl преобразует ваш код. Начнём с простого примера:
$a = $b + $c; Это преобразуется в дерево, подобное этому:
assign-to
/ \
+ $a
/ \
$b $c (но немного сложнее). Это дерево отражает способ, которым Perl проанализировал ваш код, но не имеет отношения к порядку выполнения. Существует дополнительный «поток», проходящий через узлы дерева, который показывает порядок выполнения узлов. В нашем упрощённом примере выше он выглядит так:
$b ---> $c ---> + ---> $a ---> assign-to Но с фактическим деревом компиляции для $a = $b + $c оно отличается: некоторые узлы исключены из оптимизации. В качестве следствия, хотя фактическое дерево содержит больше узлов, чем наш упрощённый пример, порядок выполнения такой же, как в нашем примере.
Просмотр дерева
Если вы скомпилировали свой perl для отладки (обычно это делается с помощью -DDEBUGGING на командной строке Configure), вы можете просмотреть скомпилированное дерево, указав -Dx в командной строке Perl. Вывод занимает несколько строк на узел, и для $b+$c он выглядит так:
5 TYPE = add ===> 6
TARG = 1
FLAGS = (SCALAR,KIDS)
{
TYPE = null ===> (4)
(was rv2sv)
FLAGS = (SCALAR,KIDS)
{
3 TYPE = gvsv ===> 4
FLAGS = (SCALAR)
GV = main::b
}
}
{
TYPE = null ===> (5)
(was rv2sv)
FLAGS = (SCALAR,KIDS)
{
4 TYPE = gvsv ===> 5
FLAGS = (SCALAR)
GV = main::c
}
} Это дерево имеет 5 узлов (по одному на каждый TYPE спецификатор), только 3 из них не исключены из оптимизации (по одному на каждое число в левом столбце). Непосредственные дочерние узлы данного узла соответствуют {} парам на том же уровне отступа, таким образом, этот список соответствует дереву:
add
/ \
null null
| |
gvsv gvsv Порядок выполнения указан с помощью ===> пометок, таким образом, это 3 4 5 6 (узел 6 не включён в вышеприведённый список), т.е., gvsv gvsv add whatever.
Каждый из этих узлов представляет собой операцию (op), фундаментальную операцию внутри ядра Perl. Код, реализующий каждую операцию, можно найти в файлах pp*.c; функция, реализующая операцию с типом gvsv, это pp_gvsv, и так далее. Как показывает дерево выше, различные операции имеют разное количество дочерних узлов: add — бинарный оператор, как можно было ожидать, и, следовательно, имеет два дочерних узла. Для адаптации к различным количествам дочерних узлов существуют различные типы структур данных операторов, и они связываются разными способами.
Простейший тип структуры оператора — OP: у него нет дочерних узлов. Унарные операторы, UNOPы, имеют один дочерний узел, и он указывается с помощью поля op_first. Бинарные операторы (BINOPы) имеют не только поле op_first, но и поле op_last. Наиболее сложный тип оператора — LISTOP, который имеет любое количество дочерних узлов. В этом случае первый дочерний узел указывается полем op_first, а последний — полем op_last. Дочерние узлы между ними можно найти, итеративно следуя указателю OpSIBLING от первого дочернего узла до последнего (но см. ниже).
Также есть и другие типы операторов: PMOP хранит регулярное выражение и не имеет дочерних узлов, а LOOP может или не может иметь дочерние узлы. Если поле op_children не равно нулю, оно ведет себя как LISTOP. Чтобы усложнить задачу, если UNOP на самом деле является оператором null после оптимизации (см. "Этап компиляции 2: распространение контекста"), он всё ещё будет иметь дочерние узлы в соответствии со своим прежним типом.
Наконец, есть LOGOP, или логический оператор. Подобно LISTOP, он имеет один или несколько дочерних узлов, но не имеет поля op_last: поэтому вам нужно следовать по цепочке op_first и затем по самой цепочке OpSIBLING, чтобы найти последний дочерний узел. Вместо этого у него есть поле op_other, которое сопоставимо с полем op_next, описанным ниже, и представляет собой альтернативный путь выполнения. Операторы, такие как and, or и ?, являются LOGOPами. Обратите внимание, что в общем случае op_other может не указывать ни на один из непосредственных дочерних узлов LOGOP.
Начиная с версии 5.21.2, perl'и, построенные с экспериментальным определением -DPERL_OP_PARENT, добавляют дополнительный булевый флаг для каждой операции, op_moresib. Когда он не установлен, это указывает на то, что это последняя операция в цепочке OpSIBLING. Это освобождает поле op_sibling на последнем смежном узле, чтобы указать обратную ссылку на родительский оператор. В этой сборке это поле также переименовано в op_sibparent, чтобы отразить его совместную роль. Макрос OpSIBLING(o) оборачивает это специальное поведение и всегда возвращает NULL для последнего смежного узла. С этой сборкой функция op_parent(o) может использоваться для поиска родительского оператора любого оператора. Таким образом, для совместимости с будущими версиями всегда следует использовать макрос OpSIBLING(o), а не обращаться к op_sibling напрямую.
Другой способ просмотреть дерево — использовать модуль компилятора заднего плана, такой как B::Concise.
Этап компиляции 1: процедуры проверки
Дерево создаётся компилятором, когда код yacc передаёт ему конструкции, которые он распознаёт. Поскольку yacc работает по принципу «сверху вниз», так же работает и первый этап компиляции perl.
То, что делает этот этап интересным для разработчиков perl, заключается в том, что на этом этапе может быть выполнена оптимизация. Это оптимизация с помощью так называемых «процедур проверки». Соответствие между именами узлов и соответствующими процедурами проверки описано в opcode.pl (не забудьте запустить make regen_headers, если вы изменяете этот файл).
Процедура проверки вызывается, когда узел полностью сформирован, за исключением потока порядка выполнения. Поскольку в этот момент нет обратных ссылок на текущий формируемый узел, можно выполнить практически любое действие над узлом верхнего уровня, включая его освобождение и/или создание новых узлов над/под ним.
Процедура проверки возвращает узел, который должен быть вставлен в дерево (если узел верхнего уровня не был изменён, процедура проверки возвращает свой аргумент).
По соглашению, процедуры проверки имеют имена ck_*. Обычно они вызываются из подпрограмм new*OP (или convert). (которые в свою очередь вызываются из perly.y).
Этап компиляции 1a: свёртка констант
Сразу после вызова процедуры проверки возвращённый узел проверяется на возможность выполнения во время компиляции. Если это так (значение считается константой), оно сразу выполняется, и вместо него подставляется узел константы со «значением возврата» соответствующего поддерева. Поддерево удаляется.
Если свёртка констант не выполнилась, создаётся поток порядка выполнения.
Этап компиляции 2: распространение контекста
Когда контекст для части дерева компиляции известен, он распространяется вниз по дереву. В этот момент контекст может принимать 5 значений (вместо 2 для контекста во время выполнения): пустое, булево, скалярное, списочное и левое значение. В отличие от этапа 1, этот этап обрабатывается сверху вниз: контекст узла определяет контекст для его дочерних узлов.
На этом этапе выполняются дополнительные оптимизации, зависящие от контекста. Поскольку в этот момент дерево компиляции содержит обратные ссылки (через указатели «потока»), узлы не могут быть освобождены (free()) в данный момент. Чтобы разрешить исключение из оптимизации узлов на этом этапе, такие узлы вместо освобождения (free()) устанавливаются в нулевое значение (т. е., их тип изменяется на 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 *first)
{
OP *o = first, *t = first;
for(; o = o->op_next, t = t->op_next) {
/* custom per-op optimisation goes here */
o = o->op_next;
if (!o || o == t) break;
/* custom per-op optimisation goes AND 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, представляющего область видимости; это указатель на указатель, так что вы можете заменить операцию, если нужно.
-
void bhk_post_end(pTHX_ OP **o) -
Вызывается в конце лексической области видимости сразу после разворачивания стека. o, как и выше. Обратите внимание, что вызовы
pre_иpost_endмогут быть вложены, если в стеке сохранения есть что-то, что вызывает строковый eval. -
void bhk_eval(pTHX_ OP *const o) -
Вызывается непосредственно перед началом компиляции
eval STRING,do FILE,requireилиuse, после того, как eval был настроен. o — OP, который запросил eval, и обычно будетOP_ENTEREVAL,OP_DOFILEилиOP_REQUIRE.
После того, как у вас есть ваши крючковые функции, вам нужна структура BHK, чтобы поместить их в неё. Лучше всего выделить её статически, так как нет способа освободить её после регистрации. Указатели на функции должны быть вставлены в эту структуру с помощью макроса BhkENTRY_set, который также установит флаги, указывающие, какие записи допустимы. Если вам всё же нужно динамически выделять BHK по какой-то причине, обязательно обнулите его, прежде чем начать.
После регистрации механизма отключения этих крючков нет, поэтому, если это необходимо, вам нужно сделать это самостоятельно. Запись в %^H — вероятно, лучший способ, так что эффект будет лексически ограниченным; однако также можно использовать макросы BhkDISABLE и BhkENABLE для временного включения и выключения записей. Также следует понимать, что обычно по крайней мере одна область видимости откроется до загрузки вашего расширения, поэтому вы увидите некоторые пары pre/post_end, у которых не было соответствующего start.
Просмотр внутренних структур данных с помощью функций dump
Для помощи в отладке, исходный файл dump.c содержит ряд функций, которые производят форматированный вывод внутренних структур данных.
Наиболее часто используемая из этих функций — Perl_sv_dump; она используется для вывода информации о SVs, AVs, HVs и CVs. Модуль Devel::Peek вызывает sv_dump для вывода отладочной информации из Perl-пространства, поэтому пользователи этого модуля уже должны быть знакомы с его форматом.
Perl_op_dump может быть использован для вывода структуры OP или любого из её производных, и производит вывод, аналогичный perl -Dx; на самом деле, Perl_dump_eval выведет главную корневую часть кода, который оценивается, точно так же, как -Dx.
Другие полезные функции — Perl_dump_sub, которая преобразует GV в дерево операций, Perl_dump_packsubs, которая вызывает Perl_dump_sub для всех подпрограмм в пакете следующим образом: (К счастью, все это xsubs, поэтому нет дерева операций)
(gdb) print Perl_dump_packsubs(PL_defstash)
SUB attributes::bootstrap = (xsub 0x811fedc 0)
SUB UNIVERSAL::can = (xsub 0x811f50c 0)
SUB UNIVERSAL::isa = (xsub 0x811f304 0)
SUB UNIVERSAL::VERSION = (xsub 0x811f7ac 0)
SUB DynaLoader::boot_DynaLoader = (xsub 0x805b188 0) и Perl_dump_all, которая выводит все подпрограммы в хранилище и дерево операций основной корневой части.
Как поддерживаются несколько интерпретаторов и конкурентность
Обзор и MULTIPLICITY
Интерпретатор Perl можно рассматривать как закрытый ящик: у него есть API для подачи ему кода или иных действий, но также есть функции для его собственного использования. Это очень похоже на объект, и есть способ создать Perl так, чтобы у него было несколько интерпретаторов, где каждый интерпретатор представлен либо C-структурой, либо внутри структуры, специфичной для потока. Эти структуры содержат весь контекст, состояние этого интерпретатора.
Макрос, который управляет основным вариантом сборки Perl, — это MULTIPLICITY. В сборке MULTIPLICITY имеется C-структура, которая упаковывает все состояние интерпретатора, передаваемое различным функциям Perl в качестве «скрытого» первого аргумента. MULTIPLICITY делает многопоточные Perl возможными (с моделью потоков ithreads, связанной с макросом USE_ITHREADS.)
PERL_IMPLICIT_CONTEXT — это устаревший синоним MULTIPLICITY.
Чтобы увидеть, есть ли у вас данные, не являющиеся константами, можно использовать совместимый с 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 были либо подпрограммами, принимающими какой-то вид структуры в качестве первого аргумента, либо подпрограммами, не принимающими первый аргумент. Для включения этих двух очень разных способов построения интерпретатора исходный код Perl (как и во многих других ситуациях) широко использует макросы и соглашения об именовании подпрограмм.
Первая проблема: определение, какие функции будут публичными функциями API, а какие — частными. Все функции, имена которых начинаются с S_, являются частными (подумайте «S» как «secret» или «static»). Все остальные функции начинаются с «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 скомпилирован без опций, устанавливающих MULTIPLICITY, нет первого аргумента, содержащего контекст интерпретатора. Завершающая подчёркивание в макросе pTHX_ указывает, что макрос расширения требует запятой после аргумента контекста, потому что за ним следуют другие аргументы. Если MULTIPLICITY не определён, pTHX_ будет проигнорирован, и подпрограмма не будет прототипирована для принятия дополнительного аргумента. Форма макроса без завершающего подчёркивания используется, когда нет дополнительных явных аргументов.
Когда одна внутренняя функция Perl вызывает другую, она должна передать контекст. Это обычно скрыто с помощью макросов. Рассмотрим sv_setiv. Он расширяется до чего-то вроде этого:
#ifdef MULTIPLICITY
#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 скомпилирован с MULTIPLICITY, расширения, которые вызывают какие-либо функции API Perl, должны каким-то образом передавать начальный аргумент контекста. Суть в том, что вам нужно написать это таким образом, чтобы расширение всё ещё компилировалось, когда Perl не был скомпилирован с включённым MULTIPLICITY.
Есть три способа сделать это. Во-первых, простой, но неэффективный способ, который также является по умолчанию, для сохранения совместимости исходного кода с расширениями: всякий раз, когда #include'd XSUB.h, он переопределяет макросы aTHX и aTHX_ для вызова функции, которая вернёт контекст. Таким образом, что-то вроде:
sv_setiv(sv, num); в вашем расширении будет преобразовано в следующее, когда MULTIPLICITY включён:
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 из нескольких потоков?
Если вы создаёте интерпретаторы в одном потоке, а затем вызываете их в другом, вам нужно убедиться, что собственный слот Thread Local Storage (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_GET_CONTEXT.)
Планы на будущее и PERL_IMPLICIT_SYS
Так же, как MULTIPLICITY предоставляет способ объединения всего, что интерпретатор знает о себе, и передачи этого, так же есть планы позволить интерпретатору объединить всё, что он знает об окружающей среде, в которой он работает. Это включено с макросом PERL_IMPLICIT_SYS. В настоящее время он работает только с USE_ITHREADS в Windows.
Это позволяет предоставить дополнительный указатель (называемый «средой хоста») для всех системных вызовов. Это делает возможным для всей системной части поддерживать своё собственное состояние, разделённое на семь C-структур. Это тонкие оболочки вокруг обычных системных вызовов (см. win32/perllib.c) для исполняемого файла Perl по умолчанию, но для более амбициозного хоста (например, такого, который бы эмулировал fork()) вся дополнительная работа, необходимая для имитации того, что различные интерпретаторы на самом деле являются различными «процессами», будет выполнена здесь.
Двигатель/интерпретатор Perl и хост — ортогональные сущности. В процессе может быть один или несколько интерпретаторов и один или несколько «хостов», со свободной ассоциацией между ними.
Внутренние функции
Все внутренние функции Perl, которые будут доступны внешнему миру, имеют префикс Perl_, чтобы они не конфликтовали с функциями XS или функциями, используемыми в программе, в которую встраивается Perl. Аналогично, все глобальные переменные начинаются с PL_. (По соглашению, статические функции начинаются с S_.)
Внутри ядра Perl (PERL_CORE определено), вы можете получить доступ к функциям с префиксом Perl_ или без него благодаря набору определений, находящихся в embed.h. Обратите внимание, что код расширения не должен устанавливать PERL_CORE; это раскрывает весь внутренний механизм Perl и, вероятно, вызовет разрыв XS в каждом новом релизе Perl.
Файл embed.h генерируется автоматически из embed.pl и embed.fnc. embed.pl также создаёт прототипирующие заголовочные файлы для внутренних функций, генерирует документацию и множество других элементов. Важно, что при добавлении новой функции в ядро или изменении существующей, вы также должны изменить данные в таблице в embed.fnc. Вот пример записи из этой таблицы:
Apd |SV** |av_fetch |AV* ar|I32 key|I32 lval Первый столбец — набор флагов, второй — тип возвращаемого значения, третий — имя. Столбцы после этого — аргументы. Флаги документированы в начале embed.fnc.
Если вы редактируете embed.pl или embed.fnc, вам потребуется запустить make regen_headers, чтобы выполнить перестройку embed.h и других автоматически сгенерированных файлов.
Форматированный вывод IVs, UVs и NVs
Если вы выводите IVs, UVs или NVs вместо кодов форматирования 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 будет расширяться до правильного формата для IVs. Обратите внимание, что пробелы вокруг формата необходимы в случае компиляции кода с C++, для сохранения соответствия стандарту.
Обратите внимание, что существуют разные «длинные двойные числа»: Perl будет использовать то, что имеет компилятор.
Если вы выводите адреса указателей, используйте %p или UVxf в сочетании с PTR2UV().
Форматированный вывод SVs
Содержимое SVs можно выводить с помощью формата SVf, например так:
Perl_croak(aTHX_ "This croaked because: %" SVf "\n", SVfARG(err_msg)) где err_msg — SV.
Не все типы скаляров подлежат выводу. Простые значения определённо подходят: IV, UV, NV или PV. Также, если SV является ссылкой на какое-либо значение, оно будет де-референсировано и выведено значение, или будет выведена информация о типе этого значения и его адресе. Результаты вывода любого другого типа SV не определены и, скорее всего, приведут к аварийному завершению интерпретатора. NVs выводятся с помощью формата, похожего на %g.
Обратите внимание, что пробелы вокруг SVf необходимы в случае компиляции кода с C++, для сохранения соответствия стандарту.
Обратите внимание, что любой файловый дескриптор, в который выполняется вывод в UTF-8, должен ожидать UTF-8, чтобы получить правильные результаты и избежать предупреждений о широких символах. Один из способов сделать это для обычных файловых дескрипторов — вызвать perl с параметром -C. (См. «-C [number/list]» в perlrun.
Вы можете использовать это для конкатенации двух скаляров:
SV *var1 = get_sv("var1", GV_ADD);
SV *var2 = get_sv("var2", GV_ADD);
SV *var3 = newSVpvf("var1=%" SVf " and var2=%" SVf,
SVfARG(var1), SVfARG(var2)); SVf_QUOTEDPREFIX аналогичен SVf, за исключением того, что он ограничивает количество выводимых символов, отображая не более первых PERL_QUOTEDPREFIX_LEN символов аргумента и выводя его в двойных кавычках с экранированным содержимым по правилам экранирования строк в двойных кавычках. Если строка длиннее этого значения, после заключительной кавычки добавляются многоточие «…». Это предназначено для сообщений об ошибках, где строка предполагается именем класса.
HvNAMEf и HvNAMEf_QUOTEDPREFIX аналогичны SVf, за исключением того, что они извлекают строку, длину и флаги utf8 из аргумента, используя макросы HvNAME(), HvNAMELEN(), HvNAMEUTF8(). Это предназначено для преобразования имени класса непосредственно из хранилища HV.
Форматированный вывод строк
Если вам нужно просто вывести байты в строке 7-битного NUL-терминированного, вы можете просто использовать %s (в предположении, что все они действительно 7-битные). Но если есть вероятность, что значение будет закодировано в UTF-8 или содержит байты выше 0x7F (и, следовательно, 8-битные), вы должны вместо этого использовать формат UTF8f. В качестве параметра используйте макрос UTF8fARG().
chr * msg;
/* U+2018: \xE2\x80\x98 LEFT SINGLE QUOTATION MARK
U+2019: \xE2\x80\x99 RIGHT SINGLE QUOTATION MARK */
if (can_utf8)
msg = "\xE2\x80\x98Uses fancy quotes\xE2\x80\x99";
else
msg = "'Uses simple quotes'";
Perl_croak(aTHX_ "The message is: %" UTF8f "\n",
UTF8fARG(can_utf8, strlen(msg), msg)); Первый параметр UTF8fARG — логический: 1, если строка в UTF-8; 0, если строка в кодировке нативных байтов (Latin1). Второй параметр — количество байтов в строке для вывода. Третий и последний параметр — указатель на первый байт в строке.
Обратите внимание, что любой файловый дескриптор, в который выполняется вывод в UTF-8, должен ожидать UTF-8, чтобы получить правильные результаты и избежать предупреждений о широких символах. Один из способов сделать это для обычных файловых дескрипторов — вызвать perl с параметром -C. (См. «-C [number/list]» в perlrun.
Форматированный вывод Size_t и SSize_t
Наиболее общий способ — преобразовать их в UV или IV и вывести, как в предыдущем разделе .
Но если вы используете PerlIO_printf(), для уменьшения набора символов и визуального мусора можно использовать модификатор длины %z (для siZe):
PerlIO_printf("STRLEN is %zu\n", len); Этот модификатор не является переносимым, поэтому его использование следует ограничить PerlIO_printf().
Форматированный вывод Ptrdiff_t, intmax_t, short и других специальных размеров
Для этих особых случаев существуют модификаторы, если вы используете PerlIO_printf(). См. "size" в perlfunc.
Указатель в целое и целое в указатель
Поскольку размер указателя не обязательно равен размеру целого числа, используйте следующие макросы, чтобы сделать это правильно.
PTR2UV(pointer)
PTR2IV(pointer)
PTR2NV(pointer)
INT2PTR(pointertotype, integer) Например:
IV iv = ...;
SV *sv = INT2PTR(SV*, iv); и
AV *av = ...;
UV uv = PTR2UV(av); Также есть
PTR2nat(pointer) /* pointer to integer of PTRSIZE */
PTR2ul(pointer) /* pointer to unsigned long */ И PTRV, которое предоставляет родной тип для целого числа того же размера, что и указатели, например, unsigned или unsigned long.
Обработка исключений
Существует несколько макросов для выполнения очень базовой обработки исключений в модулях XS. Вам необходимо определить NO_XSLOCKS перед включением XSUB.h, чтобы иметь возможность использовать эти макросы:
#define NO_XSLOCKS
#include "XSUB.h" Вы можете использовать эти макросы, если вызываете код, который может вызвать ошибку, но вам необходимо выполнить некоторые действия по очистке перед передачей управления обратно Perl. Например:
dXCPT; /* set up necessary variables */
XCPT_TRY_START {
code_that_may_croak();
} XCPT_TRY_END
XCPT_CATCH
{
/* do cleanup here */
XCPT_RETHROW;
} Обратите внимание, что вы всегда должны повторно вызывать исключение, которое было перехвачено. Используя эти макросы, невозможно просто перехватить исключение и проигнорировать его. Если вам нужно проигнорировать исключение, вам необходимо использовать функцию call_*.
Преимущество использования вышеупомянутых макросов заключается в том, что вам не нужно создавать дополнительную функцию для call_*, и использование этих макросов быстрее, чем использование call_*.
Документация источника
Ведётся работа по документированию внутренних функций и автоматическому созданию справочных руководств из них — perlapi — одно из таких руководств, в котором подробно описываются все функции, доступные авторам XS. perlintern — автоматически сгенерированное руководство по функциям, которые не являются частью API и, предположительно, предназначены только для внутреннего использования.
Документация источника создаётся путём размещения комментариев POD в исходном коде C, например, так:
/*
=for apidoc sv_setiv
Copies an integer into the given SV. Does not handle 'set' magic. See
L<perlapi/sv_setiv_mg>.
=cut
*/ Пожалуйста, постарайтесь предоставить документацию, если вы добавляете функции в ядро Perl.
Обратная совместимость
API Perl со временем меняется. Добавляются новые функции, или изменяются интерфейсы существующих функций. Модуль Devel::PPPort пытается предоставить код совместимости для некоторых из этих изменений, чтобы авторы XS не должны были кодировать его сами при поддержке нескольких версий Perl.
Devel::PPPort генерирует заголовочный файл C ppport.h, который также можно запустить как скрипт Perl. Для генерации ppport.h выполните:
perl -MDevel::PPPort -eDevel::PPPort::WriteFile Помимо проверки существующего кода XS, скрипт также может использоваться для получения информации о совместимости различных вызовов API с помощью флага командной строки --api-info . Например:
% perl ppport.h --api-info=sv_magicext Для получения подробностей см. perldoc ppport.h.
Поддержка Unicode
Perl 5.6.0 представил поддержку Unicode. Портировщикам и авторам XS важно понять эту поддержку и убедиться, что написанный ими код не портит данные Unicode.
Что такое Unicode, вообще?
В старые, менее просвещённые времена все использовали ASCII. По крайней мере, большинство из нас. Основная проблема с ASCII заключается в том, что она американская. Ну, нет, это не проблема; проблема в том, что она не особенно полезна для людей, которые не используют латинский алфавит. Раньше различные языки включали свой алфавит в верхнем диапазоне последовательности, между 128 и 255. Конечно, у нас было много вариантов, которые не были ASCII, и смысл стандарта был утерян.
Хуже того, если у вас есть язык, такой как китайский или японский, с сотнями или тысячами символов, то их действительно нельзя уместить всего в 256, поэтому они должны были забыть об ASCII и создать свои собственные системы с использованием пар чисел для ссылки на один символ.
Чтобы исправить это, некоторые люди создали Unicode, Inc. и создали новый набор символов, содержащий все символы, которые только можно вообразить, и даже больше. Существуют несколько способов представления этих символов, и тот, который использует Perl, называется UTF-8. UTF-8 использует переменное количество байтов для представления символа. Вы можете узнать больше о Unicode и модели Unicode Perl в perlunicode.
(В системах EBCDIC Perl использует вместо этого UTF-EBCDIC, который представляет собой форму UTF-8, адаптированную для систем EBCDIC. Ниже мы просто говорим о UTF-8. UTF-EBCDIC похож на UTF-8, но детали отличаются. Макросы скрывают эти различия от вас, просто помните, что конкретные числа и битовые шаблоны, представленные ниже, будут отличаться в UTF-EBCDIC.)
Как я могу распознать строку UTF-8?
Вы не можете. Это потому, что данные UTF-8 хранятся в байтах, точно так же, как и данные, не являющиеся UTF-8. Символ Unicode 200 (0xC8 для вас, работающих с шестнадцатеричными числами) — это заглавная буква E с акцентом grave — представлена двумя байтами v196.172. К сожалению, строка, не являющаяся Unicode, chr(196).chr(172), тоже имеет эту последовательность байтов. Поэтому вы не можете определить это на глаз — именно это делает ввод Unicode интересной задачей.
В общем случае, вам нужно либо знать, с чем вы имеете дело, либо нужно угадать. Функция API is_utf8_string может помочь; она скажет вам, содержит ли строка только допустимые символы UTF-8, и вероятность того, что строка, не являющаяся UTF-8, будет выглядеть как действительная строка UTF-8, очень быстро уменьшается с увеличением длины строки. В случае обработки символов по одному, isUTF8_CHAR скажет вам, является ли текущий символ в строке допустимым символом UTF-8.
Как UTF-8 представляет символы Unicode?
Как упоминалось выше, UTF-8 использует переменное количество байтов для хранения символа. Символы со значениями 0...127 хранятся в одном байте, как и хороший старый ASCII. Символ 128 хранится как v194.128; это продолжается до символа 191, который представляет собой v194.191. Сейчас мы исчерпали биты (191 — это двоичное 10111111), поэтому переходим дальше; символ 192 — это v195.128. И так далее, переходя к трём байтам на символ 2048. "Unicode 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 будут иметь установленный старший бит, поэтому вы можете проверить, нужно ли выполнить какие-то специальные действия с этим символом, например так (#%%CODE_BLOCK_1074%%% — это макрос, который проверяет, закодирован ли байт как один байт даже в 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, чтобы увидеть, как это делается в настоящее время.
Как передать строку Perl в библиотеку C?
Строка Perl, концептуально, — это непрозрачная последовательность символов. Многие библиотеки C ожидают, что их входные данные будут «классическими» строками C, которые представляют собой массивы октетов 1-255, завершённые нулевым байтом. Ваша задача при написании интерфейса между Perl и библиотекой C — определить соответствие между Perl и этой библиотекой.
В общем случае макросы SvPVbyte и родственные хорошо подходят для этой задачи. Они предполагают, что ваша строка Perl — это «строка байтов», т. е. это либо сырые, необработанные входные данные в Perl, либо уже закодированные, например, в UTF-8.
В качестве альтернативы, если ваша библиотека C ожидает текст UTF-8, вы можете использовать SvPVutf8 и родственные макросы. Это имеет тот же эффект, что и кодирование в UTF-8, а затем вызов соответствующего макроса, связанного с SvPVbyte.
Некоторые библиотеки C могут ожидать другие кодировки (например, UTF-16LE). Чтобы передать строки Perl в такие библиотеки, вы должны либо выполнить это кодирование в Perl, а затем использовать SvPVbyte, либо использовать промежуточную библиотеку C для преобразования из того, как Perl хранит строку, в желаемую кодировку.
Следите также за тем, чтобы нули в вашей строке Perl не путали библиотеку C. Если возможно, передайте длину строки в библиотеку C; если это невозможно, рассмотрите возможность отклонения строк, содержащих нулевые байты.
Что насчёт SvPV, SvPV_nolen, и т. д.?
Рассмотрим строку Perl из 3 символов $foo = "\x64\x78\x8c". Perl может хранить эти 3 символа двумя способами:
-
байты: 0x64 0x78 0x8c
-
UTF-8: 0x64 0x78 0xc2 0x8c
Теперь предположим, что вы преобразуете $foo в строку C следующим образом:
STRLEN strlen;
char *str = SvPV(foo_sv, strlen); В этот момент str может указывать на строку C длиной 3 байта или 4 байта.
В целом, мы хотим, чтобы str было одинаковым независимо от того, как Perl хранит $foo, поэтому такая неоднозначность нежелательна. SvPVbyte и SvPVutf8 решают эту проблему, предоставляя предсказуемый результат: используйте SvPVbyte если ваша библиотека C ожидает строки байтов или SvPVutf8 если она ожидает UTF-8.
Если ваша библиотека C поддерживает обе кодировки, то SvPV — всегда вместе с вызовами %%%CODE_BLOCK_1120%%! — может быть безопасным и (немного) более эффективным.
СОВЕТ ПО ТЕСТИРОВАНИЮ: Используйте функции utf8's upgrade и downgrade в своих тестах, чтобы обеспечить согласованную обработку независимо от внутренней кодировки Perl.
Как преобразовать строку в 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при обнаружении операторов этого типа оптимизатором «дыр». o — оператор, нуждающийся в оптимизации; oldop — предыдущий оптимизированный оператор, чейop_nextуказывает на o.
B::Generate напрямую поддерживает создание пользовательских операторов по имени.
Стек
В описаниях выше иногда упоминается «стек», но на самом деле в интерпретаторе Perl существует много подобных структур данных. Если не указано иное, под «стеком» обычно понимается стек значений.
Различные стеки имеют разные назначения и работают немного по-разному. Их различия отмечены ниже.
Стек значений
Этот стек хранит значения, с которыми работает обычный Perl-код, обычно промежуточные значения выражений внутри оператора. Сам стек состоит из массива указателей SV.
База этого стека указана переменной интерпретатора PL_stack_base, типа SV **.
Голова стека — PL_stack_sp, и указывает на последнюю добавленную запись.
Элементы добавляются в стек с помощью макроса PUSHs() или его вариантов, описанных выше; XPUSHs(), mPUSHs(), mXPUSHs() и типизированных версий. Обратите внимание, что нетипизированные версии этих макросов не проверяют размер стека и предполагают, что он достаточно велик. Эти макросы должны быть сопряжены с соответствующей проверкой размера стека, например, с макросом EXTEND, чтобы убедиться, что он достаточно велик. Например,
EXTEND(SP, 4);
mPUSHi(10);
mPUSHi(20);
mPUSHi(30);
mPUSHi(40); Это немного эффективнее, чем выполнение четырёх отдельных проверок в четырёх отдельных вызовах mXPUSHi().
В качестве дополнительной оптимизации производительности различные макросы PUSH работают с локальной переменной SP, а не с глобальной переменной интерпретатора PL_stack_sp. Эта переменная объявляется с помощью макроса dSP — хотя она обычно подразумевается XSUB и подобными функциями, поэтому вам редко нужно об этом задумываться напрямую. После объявления макросы PUSH будут работать только с этой локальной переменной, поэтому перед вызовом других функций ядра Perl необходимо использовать макрос PUTBACK, чтобы вернуть значение из локальной переменной SP обратно в переменную интерпретатора. Аналогично, после вызова функции ядра Perl, которая могла иметь основания передвинуть стек или добавить/удалить элементы, необходимо использовать макрос SPAGAIN, который обновляет значение локальной переменной SP из переменной интерпретатора.
Элементы извлекаются из стека с помощью макроса POPs или его типизированных версий. Также есть макрос TOPs, который проверяет верхний элемент, не удаляя его.
Обратите особое внимание на то, что указатели SV на стеке значений не влияют на общее количество ссылок на xV, на которые они ссылаются. Если создаются новые xV, которые добавляются в стек, вы должны организовать их уничтожение в подходящее время; обычно, используя один из макросов mPUSH* или sv_2mortal() для смерти xV.
Стек меток
Стек значений хранит отдельные скалярные значения Perl как временные данные между выражениями. Некоторые выражения Perl работают со списками целиком; для этого нам нужно знать, где в стеке начинается каждый список. Для этого служит стек меток.
Стек меток хранит целые числа как значения I32, которые представляют высоту стека значений до начала списка; таким образом, сама метка фактически указывает на элемент стека значений, предшествующий списку. Сам список начинается в mark + 1.
База этого стека указана переменной интерпретатора PL_markstack, типа I32 *.
Голова стека — PL_markstack_ptr, и указывает на последнюю добавленную запись.
Элементы добавляются в стек с помощью макроса PUSHMARK(). Несмотря на то, что сам стек хранит (индексы стека значений) как целые числа, макрос PUSHMARK должен получать указатель стека напрямую; он вычислит смещение индекса, сравнивая его с переменной PL_stack_sp. Таким образом, код, выполняющий эту операцию, практически всегда выглядит следующим образом:
PUSHMARK(SP); Элементы извлекаются из стека с помощью макроса POPMARK. Также есть макрос TOPMARK, который проверяет верхний элемент, не удаляя его. Эти макросы возвращают индексы I32 напрямую. Также есть макрос dMARK, который объявляет новую переменную типа SV double-pointer, называемую mark, которая указывает на ячейку стека меток; это обычно используемый макрос, который C-код будет использовать при работе со списками, указанными в стеке.
Как отмечалось выше, переменная mark сама указывает на последнее добавленное значение в стеке значений до начала списка, и поэтому сам список начинается в mark + 1. Значения списка можно перебирать с помощью такого кода, как:
for(SV **svp = mark + 1; svp <= PL_stack_sp; svp++) {
SV *item = *svp;
...
} Обратите внимание, что в случае, если список уже пуст, mark будет равно PL_stack_sp.
Поскольку переменная mark преобразуется в указатель на стек значений, следует соблюдать особую осторожность, если вызываются макросы EXTEND или любой из макросов XPUSH внутри функции, поскольку стек может потребоваться расширить, и поэтому существующий указатель теперь будет недействительным. Если это может быть проблемой, возможным решением является отслеживание смещения метки как целого числа и отслеживание самой метки позже, после перемещения стека.
I32 markoff = POPMARK;
...
SP **mark = PL_stack_base + markoff; Стек временных данных
Как отмечалось выше, ссылки xV в основном стеке значений не увеличивают счётчик ссылок xV, и поэтому используется другой механизм для отслеживания того, когда временные значения, которые хранятся в стеке, должны быть освобождены. За это отвечает стек временных данных.
Стек временных данных хранит указатели на xV, счётчики ссылок которых будут уменьшены вскоре.
База этого стека указана переменной интерпретатора PL_tmps_stack, типа SV **.
Голова стека индексируется PL_tmps_ix, целое число, хранящее индекс в массиве последнего добавленного элемента.
Нет публичного API для непосредственного добавления элементов в стек временных данных. Вместо этого используется функция API sv_2mortal() для смерти xV, добавляя его адрес в стек временных данных.
Аналогично, нет публичного API для чтения значений из стека временных данных. Вместо этого используются макросы SAVETMPS и FREETMPS. Макрос SAVETMPS устанавливает базовые уровни стека временных данных, сохраняя текущее значение PL_tmps_ix в PL_tmps_floor и сохраняя предыдущее значение в стеке сохранения. В дальнейшем, когда вызывается FREETMPS, все временные данные, добавленные после этого уровня, возвращаются.
Хотя эти два макроса часто встречаются парами в паре ENTER/ LEAVE, это не обязательно. Допускается вызывать FREETMPS несколько раз с момента последнего вызова SAVETMPS, например, в цикле, перебирающем элементы списка. Хотя вы можете вызвать SAVETMPS несколько раз в паре областей видимости, это вряд ли будет полезно. Последующие вызовы будут продвигать нижнюю границу временных данных выше, эффективно удерживая существующие временные данные до конца области видимости.
Стек сохранения
Стек сохранения используется Perl для реализации ключевого слова local и других подобных функций; любые операции очистки, которые должны быть выполнены при выходе из текущей области видимости. Элементы, добавляемые в этот стек, обычно сохраняют текущее значение какой-либо внутренней переменной или состояния, которое будет восстановлено при выходе из области видимости, return, die, goto или по другим причинам.
В то время как другие внутренние стеки Perl хранят отдельные элементы одного типа (обычно указатели SV или целые числа), элементы, помещаемые в стек сохранения, имеют множество различных типов, имея несколько полей. Например, тип SAVEt_INT должен хранить адрес переменной int, которую нужно восстановить, и значение, на которое ее нужно восстановить. Эта информация могла бы храниться с помощью полей struct, но должна была бы быть достаточно большой, чтобы хранить три указателя в самом крайнем случае, что привело бы к большому количеству потерь места в большинстве случаев с меньшими значениями.
Вместо этого, стек хранит информацию в кодировке переменной длины структур ANY. Конечное значение, помещенное в стек, хранится в поле UV, которое кодирует тип элемента, удерживаемого предыдущими элементами; количество и типы которых будут зависеть от типа хранимого элемента. Поле типа помещается последним, потому что это первое поле, которое будет извлечено при разворачивании элементов из стека.
База этого стека указывает на переменную интерпретатора PL_savestack, типа ANY *.
Вершина стека индексируется PL_savestack_ix, целым числом, которое хранит индекс в массиве, в который должен быть помещен следующий элемент. (Обратите внимание, что это отличается от большинства других стеков, которые ссылаются на самый последний помещенный элемент).
Элементы помещаются в стек сохранения с помощью различных макросов SAVE...(). Многие из этих макросов принимают переменную и сохраняют как ее адрес, так и текущее значение в стеке сохранения, гарантируя, что значение восстанавливается при выходе из области видимости.
SAVEI8(i8)
SAVEI16(i16)
SAVEI32(i32)
SAVEINT(i)
... Также существует множество других макросов специального назначения, которые сохраняют определенные типы или значения, представляющие интерес. SAVETMPS уже упоминалось выше. Другие включают SAVEFREEPV, который организует освобождение PV (т.е. буфера строки) или SAVEDESTRUCTOR, который организует вызов заданного указателя функции при выходе из области видимости. Полный список таких макросов можно найти в файле scope.h.
Нет публичного API для извлечения отдельных значений или элементов из стека сохранения. Вместо этого, через стек области видимости, пара ENTER и LEAVE формирует способ начала и остановки вложенных областей видимости. Выход из вложенной области видимости через LEAVE восстановит все сохраненные значения, которые были помещены в стек с момента последнего вызова ENTER.
Стек областей видимости
Как и в случае со стеком отметок со стеком значений, стек области видимости образует пару со стеком сохранения. Стек области видимости хранит высоту стека сохранения, в которой начинаются вложенные области видимости, и позволяет разворачивать стек сохранения обратно к этому моменту, когда область видимости покидается.
При построении Perl с включенным отладчиком, в этом стеке есть вторая часть, хранящая удобочитаемые строковые имена, описывающие тип контекста стека. Каждая операция добавления в стек сохраняет имя, а также высоту стека сохранения, а каждая операция удаления из стека проверяет имя на вершине стека с ожидаемым значением, вызывая ошибку проверки, если имя не совпадает.
База этого стека указывает на переменную интерпретатора PL_scopestack, типа I32 *. Если включено, имена стека области видимости хранятся в отдельном массиве, на который указывает PL_scopestack_name, типа const char **.
Вершина стека индексируется PL_scopestack_ix, целым числом, которое хранит индекс массива или массивов, в который должен быть помещен следующий элемент. (Обратите внимание, что это отличается от большинства других стеков, которые ссылаются на самый последний помещенный элемент).
Значения помещаются в стек области видимости с помощью макроса ENTER, который начинает новую вложенную область видимости. Любые элементы, помещенные в стек сохранения, восстанавливаются при следующем вложенном вызове макроса LEAVE.
Динамическая область видимости и стек контекстов
Примечание: этот раздел описывает непубличный внутренний 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 или tie, в стек помещается новый стек, а затем извлекается в конце.
Обратите внимание, что 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 на вершину этих элементов вместо этого.
Обратите внимание на раннее захват 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 будет устранён.
Забывание блока при присоединении корня не строго необходимо, но предотвращает возможные проблемы с перезаписью CvROOT. Повсюду в коде, как в ядре, так и на CPAN, есть код, который выполняет действия с CvROOT, поэтому забывание блока делает вещи более надёжными и предотвращает потенциальные проблемы.
Поскольку CV берёт на себя ответственность за свой блок при установке флага, этот флаг никогда не копируется при клонировании CV, так как один CV может освободить блок, на который другой CV всё ещё указывает, поскольку принудительное освобождение операторов игнорирует счётчик ссылок (но проверяет, что он выглядит правильно).
Чтобы избежать фрагментации блока, освобождённые операторы помечаются как освобождённые и прикрепляются к цепочке освобождённых операторов блока (идея позаимствована из DBM::Deep). Эти освобождённые операторы повторно используются по возможности. Отсутствие повторного использования освобождённых операторов было бы проще, но это привело бы к значительному увеличению использования памяти для программ с большими блоками if (DEBUG) {...}.
SAVEFREEOP немного проблематичен в этой схеме. Иногда он может привести к освобождению оператора после его CV. Если CV принудительно освободил операторы в своём блоке и сам блок, то мы будем работать с освобождённым блоком. Преобразование SAVEFREEOP в пустую операцию не помогает, так как иногда оператор может быть освобождён при отсутствии ошибки компиляции, поэтому оператор никогда не будет освобождён. Он держит счётчик ссылок на блок, поэтому весь блок утечёт. Поэтому SAVEFREEOP теперь устанавливает специальный флаг на операторе (->op_savefree). Принудительное освобождение операторов после ошибки компиляции не освободит ни один оператор, помеченный таким образом.
Поскольку многие части кода создают крошечные подпрограммы, состоящие всего из нескольких операторов, и поскольку огромный блок был бы довольно обременительным для таких подпрограмм, первый блок всегда очень мал. Чтобы избежать выделения слишком многих блоков для одного CV, каждый последующий блок вдвое больше предыдущего.
Smartmatch ожидает, что сможет выделить оператор во время выполнения, запустить его и затем удалить. Для этого оператор просто выделяется с помощью malloc, когда PL_compcv не настроен. Поэтому все операторы, выделенные на основе блоков, отмечаются как таковые (->op_slabbed), чтобы отличать их от операторов, выделенных с помощью malloc.
АВТОРЫ
До мая 1997 года этот документ поддерживал Джефф Окамото <okamoto@corp.hp.com>. В настоящее время он поддерживается в составе самого Perl разработчиками Perl 5 <perl5-porters@perl.org>.
При значительной помощи и предложениях от Дина Роэриха, Малкольма Бити, Андреаса Кёнига, Пола Хадсона, Ильи Захаревича, Пола Маркесса, Нила Бауэрса, Мэтью Грина, Тима Бэнса, Паука Боардмана, Ульриха Пфейфера, Стивена МакКаманта и Гурусами Сарати.
См. также
perlapi, perlintern, perlxs, perlembed
© 1993–2023 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.38.0/perlguts