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 - Введение в API Perl
ОПИСАНИЕ
В этом документе пытаются описать, как использовать API Perl, а также предоставить некоторую информацию о базовой работе ядра 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, а не от наличия других нулевых символов.
Аргументы 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 для некоторых методов, которые Perl предоставляет для проверки валидности 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необходимо обращаться в хранилище потоков (thread-local storage) в потоковом Perl. В любом случае, помните, что Perl допускает произвольные строки данных, которые могут содержать нулевые символы и не должны завершатьсяNUL.Также помните, что C не позволяет вам безопасно написать
foo(SvPVbyte(s, len), len);. Это может работать с вашим компилятором, но не со всеми. Разбейте такое выражение на отдельные присваивания:SV *s; STRLEN len; char *ptr; ptr = SvPVbyte(s, len); foo(ptr, len); -
Если вы хотите узнать, является ли скалярное значение ИСТИНОЙ, вы можете использовать:
SvTRUE(SV*) Хотя Perl автоматически увеличивает размер строк, если вам нужно принудительно заставить Perl выделить больше памяти для вашего SV, вы можете использовать макрос
SvGROW(SV*, STRLEN newlen) , который определит, нужно ли выделять больше памяти. В таком случае он вызовет функцию sv_grow. Обратите внимание, что SvGROW может только увеличивать, но не уменьшать выделенную память SV и что он не автоматически добавляет место для заключительного NUL байта (собственные функции строк Perl обычно SvGROW(sv, len + 1)).
Если вы хотите записать в буфер существующего SV и установить его значение в строку, используйте 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 для соответствия заключительного нулевого символа:
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*. Убедитесь, что вы не пытаетесь сравнивать случайный 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 могут быть удалены, если это необходимо.
После создания 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 возвращает наибольшее значение индекса в массиве (точно так же, как $#массив в 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, если переменная не существует.
См. "Понимание магии связанных хешей и массивов" для получения дополнительной информации о том, как использовать функции доступа к массивам для связанных массивов.
Работа с HV
Для создания HV используйте следующую процедуру:
HV* newHV(); После создания HV возможны следующие операции с ним:
SV** hv_store(HV*, const char* key, U32 klen, SV* val, U32 hash);
SV** hv_fetch(HV*, const char* key, U32 klen, I32 lval); Параметр klen – длина ключа, передаваемого в (Обратите внимание, что вы не можете передать 0 в качестве значения klen, чтобы сказать Perl измерить длину ключа). Аргумент val содержит указатель SV на скаляр, хранимый, и hash – предварительно вычисленное значение хеша (ноль, если вы хотите, чтобы hv_store вычислило его за вас). Параметр lval указывает, является ли этот запрос фактически частью операции записи, в этом случае новое неопределенное значение будет добавлено в HV с предоставленным ключом, и hv_fetch вернётся так, как если бы значение уже существовало.
Помните, что hv_store и hv_fetch возвращают SV** , а не просто SV*. Для доступа к скалярному значению необходимо сначала разыменовать возвращаемое значение. Однако вы должны убедиться, что возвращаемое значение не NULL перед его разыменованием.
Первая из этих двух функций проверяет, существует ли запись в хеш-таблице, а вторая удаляет её.
bool hv_exists(HV*, const char* key, U32 klen);
SV* hv_delete(HV*, const char* key, U32 klen, I32 flags); Если flags не содержит флаг G_DISCARD, то hv_delete создаст и вернёт смертную копию удалённого значения.
И ещё несколько различных функций:
void hv_clear(HV*);
void hv_undef(HV*); Подобно своим аналогам AV, hv_clear удаляет все записи в хеш-таблице, но не удаляет саму хеш-таблицу. hv_undef удаляет все записи и саму хеш-таблицу.
Perl хранит фактические данные в связанном списке структур с типом typedef HE. Эти структуры содержат фактические указатели на ключ и значение (плюс дополнительную административную нагрузку). Ключ – это указатель на строку; значение – SV*. Однако, как только у вас есть HE*, для получения фактических ключа и значения используйте указанные ниже процедуры.
I32 hv_iterinit(HV*);
/* Prepares starting point to traverse hash table */
HE* hv_iternext(HV*);
/* Get the next entry, and return a pointer to a
structure that has both the key and value */
char* hv_iterkey(HE* entry, I32* retlen);
/* Get the key from an HE structure and also return
the length of the key string */
SV* hv_iterval(HV*, HE* entry);
/* Return an SV pointer to the value of the HE
structure */
SV* hv_iternextsv(HV*, char** key, I32* retlen);
/* This convenience routine combines hv_iternext,
hv_iterkey, and hv_iterval. The key and retlen
arguments are return values for the key and its
length. The value is returned in the SV* argument */ Если вам известен идентификатор переменной хеша, вы можете получить указатель на её HV, используя следующее:
HV* get_hv("package::varname", 0); Возвращает NULL, если переменная не существует.
Алгоритм хеширования определен в макросе PERL_HASH:
PERL_HASH(hash, key, klen) Точная реализация этого макроса зависит от архитектуры и версии Perl, и возвращаемое значение может изменяться при каждом вызове, поэтому значение является действительным только в течение одного процесса Perl.
См. "Понимание магии связанных хешей и массивов" для получения дополнительной информации о том, как использовать функции доступа к хешам для связанных хешей.
Расширения API хешей
Начиная с версии 5.004, поддерживаются также следующие функции:
HE* hv_fetch_ent (HV* tb, SV* key, I32 lval, U32 hash);
HE* hv_store_ent (HV* tb, SV* key, SV* val, U32 hash);
bool hv_exists_ent (HV* tb, SV* key, U32 hash);
SV* hv_delete_ent (HV* tb, SV* key, I32 flags, U32 hash);
SV* hv_iterkeysv (HE* entry); Обратите внимание, что эти функции принимают SV* ключи, что упрощает написание кода расширения, работающего со структурами хешей. Эти функции также позволяют передавать SV* ключи функциям tie без принудительного преобразования ключей в строки (в отличие от предыдущего набора функций).
Они также возвращают и принимают целые записи хешей (HE* ), что делает их использование более эффективным (так как номер хеша для конкретной строки не нужно вычислять каждый раз). См. perlapi для подробных описаний.
Следующие макросы всегда должны использоваться для доступа к содержимому записей хешей. Обратите внимание, что аргументы этих макросов должны быть простыми переменными, так как они могут быть оценены более одного раза. См. perlapi для подробных описаний этих макросов.
HePV(HE* he, STRLEN len)
HeVAL(HE* he)
HeHASH(HE* he)
HeSVKEY(HE* he)
HeSVKEY_force(HE* he)
HeSVKEY_set(HE* he, SV* sv) Эти два макроса более низкого уровня определены, но должны использоваться только при работе с ключами, которые не являются SV*:
HeKEY(HE* he)
HeKLEN(HE* he) Обратите внимание, что hv_store и hv_store_ent не увеличивают счетчик ссылок хранимого val, что является обязанностью вызывающей функции. Если эти функции возвращают значение NULL, вызывающая функция обычно должна уменьшить счетчик ссылок val для предотвращения утечки памяти.
AV, HV и неопределенные значения
Иногда вам нужно хранить неопределенные значения в AV или HV. Хотя это может быть редкий случай, он может быть сложным. Это потому, что вы привыкли использовать &PL_sv_undef, если вам нужно неопределенное SV.
Например, интуиция подсказывает, что этот код XS:
AV *av = newAV();
av_store( av, 0, &PL_sv_undef ); эквивалентен этому коду Perl:
my @av;
$av[0] = undef;К сожалению, это не так. В Perl 5.18 и более ранних версиях AV используют &PL_sv_undef в качестве маркера, чтобы указать, что элемент массива ещё не инициализирован. Таким образом, exists $av[0] было бы истинным для приведённого выше кода Perl, но ложным для массива, сгенерированного кодом XS. В Perl 5.20 хранение &PL_sv_undef создаст элемент только для чтения, потому что хранится сам скаляр &PL_sv_undef, а не его копия.
Аналогичные проблемы могут возникнуть при хранении &PL_sv_undef в HVs:
hv_store( hv, "key", 3, &PL_sv_undef, 0 ); Это действительно установит значение undef, но если вы попытаетесь изменить значение key, вы получите следующую ошибку:
Modification of non-creatable hash value attempted В Perl 5.8.0, &PL_sv_undef также использовался для маркировки заполнителей в ограниченных хешах. Это приводило к тому, что такие записи хеша не отображались при итерации по хешу или при проверке ключей с помощью функции hv_exists.
Вы можете столкнуться с аналогичными проблемами, когда вы сохраняете &PL_sv_yes или &PL_sv_no в AV или HV. Попытка изменить такие элементы приведёт к следующей ошибке:
Modification of a read-only value attempted Короче говоря, вы можете использовать специальные переменные &PL_sv_undef, &PL_sv_yes и &PL_sv_no с AV и HV, но вам нужно убедиться, что вы понимаете, что делаете.
В общем случае, если вы хотите сохранить неопределённое значение в AV или HV, вам не следует использовать &PL_sv_undef, а вместо этого создать новое неопределённое значение с помощью функции newSV, например:
av_store( av, 42, newSV(0) );
hv_store( hv, "foo", 3, newSV(0), 0 ); Ссылки
Ссылки — это специальный тип скаляров, которые указывают на другие типы данных (включая другие ссылки).
Для создания ссылки используйте одну из следующих функций:
SV* newRV_inc((SV*) thing);
SV* newRV_noinc((SV*) thing); Аргумент thing может быть любым SV*, AV*, или HV*. Функции идентичны, за исключением того, что newRV_inc увеличивает счётчик ссылок thing, в то время как newRV_noinc — нет. По историческим причинам, newRV является синонимом для newRV_inc.
После получения ссылки вы можете использовать следующую макрокоманду для разыменования ссылки:
SvRV(SV*) затем вызовите соответствующие функции, преобразовав возвращаемый SV* в AV* или HV*, если это необходимо.
Чтобы определить, является ли SV ссылкой, вы можете использовать следующую макрокоманду:
SvROK(SV*) Чтобы узнать, на какой тип значения указывает ссылка, используйте следующую макрокоманду и затем проверьте возвращаемое значение.
SvTYPE(SvRV(SV*)) Наиболее полезные типы, которые будут возвращены:
SVt_PVAV Array
SVt_PVHV Hash
SVt_PVCV Code
SVt_PVGV Glob (possibly a file handle) Любое численное значение, возвращаемое, которое меньше SVt_PVAV, будет скаляром какой-либо формы.
См. "svtype" в perlapi для получения дополнительной информации.
Благословенные ссылки и объекты классов
Ссылки также используются для поддержки объектно-ориентированного программирования. В лексиконе ОО Perl объект — это просто ссылка, которая была благословлена в пакет (или класс). После благословения программист может использовать ссылку для доступа к различным методам в классе.
Ссылка может быть благословлена в пакет с помощью следующей функции:
SV* sv_bless(SV* sv, HV* stash); Аргумент sv должен быть значением ссылки. Аргумент stash определяет, к какому классу будет принадлежать ссылка. См. "Хранилища и глобальные переменные" для получения информации о преобразовании имён классов в хранилища.
/* Ещё в стадии разработки */
Следующая функция повышает rv до ссылки, если это не ссылка. Создаёт новый SV для rv, чтобы указать на него. Если classname не равно нулю, SV благословляется в указанный класс. Возвращается SV.
SV* newSVrv(SV* rv, const char* classname); Следующие три функции копируют целое число, целое число без знака или двойное значение в SV, ссылка на который rv. SV благословляется, если classname не равно нулю.
SV* sv_setref_iv(SV* rv, const char* classname, IV iv);
SV* sv_setref_uv(SV* rv, const char* classname, UV uv);
SV* sv_setref_nv(SV* rv, const char* classname, NV iv); Следующая функция копирует значение указателя (адрес, а не строку!) в SV, ссылка на который rv. SV благословляется, если classname не равно нулю.
SV* sv_setref_pv(SV* rv, const char* classname, void* pv); Следующая функция копирует строку в SV, ссылка на который rv. Установите длину в 0, чтобы позволить Perl рассчитать длину строки. SV благословляется, если classname не равно нулю.
SV* sv_setref_pvn(SV* rv, const char* classname, char* pv,
STRLEN length); Следующая функция проверяет, благословен ли SV в указанный класс. Она не проверяет отношения наследования.
int sv_isa(SV* sv, const char* name); Следующая функция проверяет, является ли SV ссылкой на благословенный объект.
int sv_isobject(SV* sv); Следующая функция проверяет, является ли SV производным от указанного класса. SV может быть либо ссылкой на благословенный объект, либо строкой, содержащей имя класса. Это функция, реализующая функциональность UNIVERSAL::isa.
bool sv_derived_from(SV* sv, const char* name); Чтобы проверить, имеете ли вы объект, производный от определённого класса, нужно написать:
if (sv_isobject(sv) && sv_derived_from(sv, class)) { ... } Создание новых переменных
Чтобы создать новую переменную Perl с неопределённым значением, к которой можно обратиться из вашего скрипта 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 (с неопределённым значением), единственная ссылка которого является смертной. sv_mortalcopy() создаёт xV, значение которого является копией переданного xV, и единственная ссылка которого является смертной. sv_2mortal() делает существующую ссылку на xV смертной: она передаёт владение ссылкой от вызывающего контекста в стек временных переменных. Поскольку sv_newmortal не присваивает новому SV никакого значения, ему обычно необходимо присвоить значение с помощью sv_setpv, sv_setiv и т. д.:
SV *tmp = sv_newmortal();
sv_setiv(tmp, an_integer); Поскольку это несколько инструкций C, довольно часто используется такой идиоматический код:
SV *tmp = sv_2mortal(newSViv(an_integer)); Процедуры создания смертных ссылок подходят не только для SV; AV и HV могут быть сделаны смертными, передав адрес (преобразованный к типу SV*) функциям sv_2mortal или sv_mortalcopy.
Хранилища и глобы
Хранилище — это хеш, содержащий все переменные, определённые внутри пакета. Каждый ключ хранилища — это имя символа (общее для всех различных типов объектов с одинаковым именем), а каждое значение в таблице хешей — это GV (Глобальное значение). Это GV, в свою очередь, содержит ссылки на различные объекты с таким именем, включая (но не ограничиваясь):
Scalar Value
Array Value
Hash Value
I/O Handle
Format
Subroutine Существует единственное хранилище, называемое PL_defstash, которое хранит элементы, существующие в пакете main. Для доступа к элементам других пакетов добавьте строку "::" к имени пакета. Элементы пакета Foo находятся в хранилище Foo:: в PL_defstash. Элементы пакета Bar::Baz находятся в хранилище Baz:: в хранилище пакета Bar::.
Для получения указателя на хранилище для определённого пакета используйте функцию:
HV* gv_stashpv(const char* name, I32 flags)
HV* gv_stashsv(SV*, I32 flags) Первая функция принимает строку-литерал, вторая использует строку, хранящуюся в SV. Помните, что хранилище — это просто таблица хешей, поэтому вы получите обратно HV*. Флаг flags создаст новый пакет, если он установлен в GV_ADD.
Имя, которое gv_stash*v хочет получить, — это имя пакета, таблица символов которого вы хотите получить. По умолчанию используется пакет main. Если у вас есть вложенные пакеты, передайте их имена в gv_stash*v, разделённые ::, как в самом языке Perl.
В качестве альтернативы, если у вас есть SV, являющийся благословлённой ссылкой, вы можете найти указатель на хранилище, используя:
HV* SvSTASH(SvRV(SV*)); Затем используйте следующее, чтобы получить само имя пакета:
char* HvNAME(HV* stash); Если вам нужно благословить или повторно благословить объект, вы можете использовать следующую функцию:
SV* sv_bless(SV*, HV* stash) где первый аргумент, SV*, должен быть ссылкой, а второй — хранилищем. Возвращённый SV* теперь можно использовать так же, как и любой другой SV.
Для получения дополнительной информации о ссылках и благословениях обратитесь к perlref.
Дескрипторы ввода/вывода
Как 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 был бы обратным, то вместо SvIOK_on необходимо было бы вызвать макрос SvPOK_on.
Значения только для чтения
В Perl 5.16 и более ранних версиях копирование при записи (см. следующий раздел) использовало тот же бит флага, что и скаляры только для чтения. Таким образом, единственный способ проверить, что sv_setsv, и т. д., вызовут ошибку "Изменение значения только для чтения" в этих версиях, состоит в следующем:
SvREADONLY(sv) && !SvIsCOW(sv) В Perl 5.18 и более поздних версиях SvREADONLY применяется только к переменным только для чтения, а в 5.20 скаляры с копированием при записи также могут быть только для чтения, поэтому вышеприведённая проверка некорректна. Вам просто нужно:
SvREADONLY(sv) Если вам часто нужно выполнять эту проверку, определите свой собственный макрос, как показано ниже:
#if PERL_VERSION >= 18
# define SvTRULYREADONLY(sv) SvREADONLY(sv)
#else
# define SvTRULYREADONLY(sv) (SvREADONLY(sv) && !SvIsCOW(sv))
#endif Копирование при записи
Perl реализует механизм копирования при записи (COW) для скаляров, при котором копии строк не создаются немедленно по запросу, а откладываются до тех пор, пока одна или обе скалярные переменные не изменятся. Это в основном прозрачно, но необходимо следить за тем, чтобы не изменять буферы строк, которые совместно используются несколькими SV.
Вы можете проверить, использует ли SV механизм копирования при записи, используя SvIsCOW(sv).
Вы можете принудительно заставить SV создать свою собственную копию буфера строки, вызвав sv_force_normal(sv) или SvPV_force_nolen(sv).
Если вы хотите заставить SV освободить свой буфер строки, используйте sv_force_normal_flags(sv, SV_COW_DROP_PV) или просто sv_setsv(sv, NULL).
Все эти функции будут вызывать ошибку для скаляров только для чтения (см. предыдущий раздел для получения дополнительной информации об этом).
Чтобы проверить, что ваш код работает правильно и не изменяет буферы COW, на системах, поддерживающих mmap(2) (то есть Unix), вы можете сконфигурировать Perl с -Accflags=-DPERL_DEBUG_READONLY_COW, и это преобразует нарушения буфера в сбои. Вы обнаружите, что это невероятно медленно, поэтому вы можете пропустить собственные тесты Perl.
Магические переменные
[Этот раздел ещё находится в стадии разработки. Игнорируйте всё здесь. Не размещайте объявления. Всё, что запрещено, запрещено.]
Любой SV может быть магическим, то есть он имеет особые функции, которых нет у обычного SV. Эти функции хранятся в структуре SV в связанном списке struct magic'ов, тип которых определён как MAGIC.
struct magic {
MAGIC* mg_moremagic;
MGVTBL* mg_virtual;
U16 mg_private;
char mg_type;
U8 mg_flags;
I32 mg_len;
SV* mg_obj;
char* mg_ptr;
}; Обратите внимание, что это текущая версия от патча 0 и может измениться в любое время.
Присваивание магии
Perl добавляет магию к SV с помощью функции sv_magic:
void sv_magic(SV* sv, SV* obj, int how, const char* name, I32 namlen); Аргумент sv — это указатель на SV, которому нужно добавить новую магическую функцию.
Если sv ещё не является магическим, Perl использует макрос SvUPGRADE для преобразования sv в тип SVt_PVMG. Затем Perl продолжает, добавляя новую магию в начало связанного списка магических функций. Любой предыдущий элемент того же типа магии удаляется. Обратите внимание, что это может быть переопределено, и с одним SV может быть связано несколько экземпляров магии одного типа.
Аргументы name и namlen используются для сопоставления строки с магией, обычно имени переменной. namlen хранится в поле mg_len, и если name не равно NULL, то либо копия savepvn name, либо само name хранятся в поле mg_ptr, в зависимости от того, больше ли namlen нуля или равно ему. В качестве специального случая, если (name && namlen == HEf_SVKEY), то предполагается, что name содержит SV*, и он хранится как есть с увеличенным REFCNT.
Функция sv_magic использует how для определения того, какой, если есть, предопределённый "Магический виртуальный стол" следует присвоить полю mg_virtual . См. раздел "Магические виртуальные таблицы" ниже. Аргумент how также хранится в поле mg_type . Значение how должно выбираться из набора макросов PERL_MAGIC_foo, представленных в perl.h. Обратите внимание, что до добавления этих макросов внутренности Perl использовали непосредственно символьные литералы, поэтому иногда можно встретить старый код или документацию, в которой упоминается 'U' magic вместо PERL_MAGIC_uvar и т. д.
Аргумент obj хранится в поле mg_obj структуры MAGIC. Если он не совпадает с аргументом sv, счётчик ссылок на объект obj увеличивается. Если они совпадают, или если аргумент how равен PERL_MAGIC_arylen, PERL_MAGIC_regdatum, PERL_MAGIC_regdata, или если это указатель NULL, то obj просто хранится без увеличения счётчика ссылок.
См. также sv_magicext в perlapi для более гибкого способа добавления магии к SV.
Также существует функция для добавления магии к HV:
void hv_magic(HV *hv, GV *gv, int how); Это просто вызов sv_magic и приведение аргумента gv к типу SV.
Чтобы удалить магию из SV, вызовите функцию sv_unmagic:
int sv_unmagic(SV *sv, int type); Аргумент type должен быть равен значению how при первоначальном применении магии к SV.
Однако, обратите внимание, что sv_unmagic удаляет всю магию определенного типа type из SV. Если вы хотите удалить только определённую магию объекта type на основе виртуальной таблицы магии, используйте sv_unmagicext вместо этого:
int sv_unmagicext(SV *sv, int type, MGVTBL *vtbl); Виртуальные таблицы магии
Поле mg_virtual в структуре MAGIC является указателем на MGVTBL, структуру указателей на функции, представляющую "Виртуальную таблицу магии" для обработки различных операций, которые могут быть применены к этой переменной.
В структуре MGVTBL содержатся пять (или иногда восемь) указателей на следующие типы процедур:
int (*svt_get) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_set) (pTHX_ SV* sv, MAGIC* mg);
U32 (*svt_len) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_clear)(pTHX_ SV* sv, MAGIC* mg);
int (*svt_free) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_copy) (pTHX_ SV *sv, MAGIC* mg, SV *nsv,
const char *name, I32 namlen);
int (*svt_dup) (pTHX_ MAGIC *mg, CLONE_PARAMS *param);
int (*svt_local)(pTHX_ SV *nsv, MAGIC *mg); Структура MGVTBL устанавливается на этапе компиляции в perl.h и в настоящее время насчитывает 32 типа. Эти различные структуры содержат указатели на различные процедуры, которые выполняют дополнительные действия в зависимости от вызываемой функции.
Function pointer Action taken
---------------- ------------
svt_get Do something before the value of the SV is
retrieved.
svt_set Do something after the SV is assigned a value.
svt_len Report on the SV's length.
svt_clear Clear something the SV represents.
svt_free Free any extra storage associated with the SV.
svt_copy copy tied variable magic to a tied element
svt_dup duplicate a magic structure during thread cloning
svt_local copy magic to local value during 'local' Например, структура MGVTBL с именем vtbl_sv (которая соответствует типу магии mg_type для PERL_MAGIC_sv) содержит:
{ magic_get, magic_set, magic_len, 0, 0 } Таким образом, когда SV определяется как магический и имеет тип PERL_MAGIC_sv, при выполнении операции получения вызывается процедура magic_get. Все различные процедуры для различных типов магии начинаются с magic_. ПРИМЕЧАНИЕ: процедуры магии не считаются частью Perl API и могут не экспортироваться библиотекой Perl.
Последние три слота — недавнее добавление, и для совместимости исходного кода они проверяются только в том случае, если один из трех флагов MGf_COPY, MGf_DUP, или MGf_LOCAL установлен в mg_flags. Это означает, что большинство кода может продолжать объявлять vtable как значение из 5 элементов. Эти три флага в настоящее время используются исключительно кодом потоков и могут быть изменены.
Текущие виды виртуальных таблиц магии:
mg_type
(old-style char and macro) MGVTBL Type of magic
-------------------------- ------ -------------
\0 PERL_MAGIC_sv vtbl_sv Special scalar variable
# PERL_MAGIC_arylen vtbl_arylen Array length ($#ary)
% PERL_MAGIC_rhash (none) Extra data for restricted
hashes
* PERL_MAGIC_debugvar vtbl_debugvar $DB::single, signal, trace
vars
. PERL_MAGIC_pos vtbl_pos pos() lvalue
: PERL_MAGIC_symtab (none) Extra data for symbol
tables
< PERL_MAGIC_backref vtbl_backref For weak ref data
@ PERL_MAGIC_arylen_p (none) To move arylen out of XPVAV
B PERL_MAGIC_bm vtbl_regexp Boyer-Moore
(fast string search)
c PERL_MAGIC_overload_table vtbl_ovrld Holds overload table
(AMT) on stash
D PERL_MAGIC_regdata vtbl_regdata Regex match position data
(@+ and @- vars)
d PERL_MAGIC_regdatum vtbl_regdatum Regex match position data
element
E PERL_MAGIC_env vtbl_env %ENV hash
e PERL_MAGIC_envelem vtbl_envelem %ENV hash element
f PERL_MAGIC_fm vtbl_regexp Formline
('compiled' format)
g PERL_MAGIC_regex_global vtbl_mglob m//g target
H PERL_MAGIC_hints vtbl_hints %^H hash
h PERL_MAGIC_hintselem vtbl_hintselem %^H hash element
I PERL_MAGIC_isa vtbl_isa @ISA array
i PERL_MAGIC_isaelem vtbl_isaelem @ISA array element
k PERL_MAGIC_nkeys vtbl_nkeys scalar(keys()) lvalue
L PERL_MAGIC_dbfile (none) Debugger %_<filename
l PERL_MAGIC_dbline vtbl_dbline Debugger %_<filename
element
N PERL_MAGIC_shared (none) Shared between threads
n PERL_MAGIC_shared_scalar (none) Shared between threads
o PERL_MAGIC_collxfrm vtbl_collxfrm Locale transformation
P PERL_MAGIC_tied vtbl_pack Tied array or hash
p PERL_MAGIC_tiedelem vtbl_packelem Tied array or hash element
q PERL_MAGIC_tiedscalar vtbl_packelem Tied scalar or handle
r PERL_MAGIC_qr vtbl_regexp Precompiled qr// regex
S PERL_MAGIC_sig 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_substr vtbl_substr substr() lvalue
Y PERL_MAGIC_nonelem vtbl_nonelem Array element that does not
exist
y PERL_MAGIC_defelem vtbl_defelem Shadow "foreach" iterator
variable / smart parameter
vivification
\ PERL_MAGIC_lvref vtbl_lvref Lvalue reference
constructor
] PERL_MAGIC_checkcall vtbl_checkcall Inlining/mutation of call
to this CV
~ PERL_MAGIC_ext (none) Available for use by
extensions Когда в таблице встречаются как прописная, так и строчная буквы, заглавная буква обычно используется для представления какого-либо составного типа (список или хеш), а строчная буква — для представления элемента этого составного типа. Некоторые внутренние части кода используют это соотношение регистров. Однако 'v' и 'V' (вектор и строка V) никак не связаны.
Типы магии PERL_MAGIC_ext и PERL_MAGIC_uvar определены специально для использования расширениями и не будут использоваться самим perl. Расширения могут использовать магию типа PERL_MAGIC_ext для «прикрепления» частной информации к переменным (обычно к объектам). Это особенно полезно, потому что обычный код perl не может повредить эту частную информацию (в отличие от использования дополнительных элементов объекта хеша).
Аналогично, магия типа 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 к массивам разрешено, но не имеет эффекта.
Для хешей есть специализированный обработчик, который даёт контроль над ключами хешей (но не над значениями). Этот обработчик вызывает магию получения PERL_MAGIC_uvar, если функция «установки» в структуре 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*(), описанные ранее, не вызывают магию «установки» для своих целевых объектов. Это должно быть выполнено пользователем путём вызова макроса SvSETMAGIC() после вызова этих функций или путём использования одной из функций sv_set*_mg() или sv_cat*_mg(). Аналогично, общий код C должен вызвать макрос SvGETMAGIC(), чтобы вызвать любую магию «получения», если они используют SV, полученные из внешних источников в функциях, которые не обрабатывают магию. Для описания этих функций см. perlapi. Например, вызовы функций sv_cat*() обычно требуют последующего вызова SvSETMAGIC(), но не требуют предварительного вызова SvGETMAGIC(), так как их реализация обрабатывает магию «получения».
Поиск магии
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 при использовании единообразного синтаксиса хешей и массивов. Использование этого синтаксического сахара накладывает определённую нагрузку (как правило, около двух-четырёх дополнительных инструкций opcodes на операцию FETCH/STORE, помимо создания всех требуемых смертных переменных для вызова методов). Эта нагрузка будет относительно небольшой, если методы TIE сами по себе значительны, но если они состоят только из нескольких операторов, нагрузка не будет незначительной.
Локализация изменений
В Perl есть очень удобная конструкция:
{
local $var = 2;
...
} Эта конструкция приблизительно эквивалентна
{
my $oldvar = $var;
$var = 2;
...
$var = $oldvar;
} Основное различие заключается в том, что первая конструкция восстановит исходное значение $var независимо от того, каким образом завершится выполнение блока: goto, return, die/eval, и т.д. Также она немного эффективнее.
Существует способ выполнения аналогичной задачи из C через Perl API: создать псевдо-блок и организовать автоматическое отмену некоторых изменений в конце блока, либо явно, либо через нелокальный выход (через die()). Конструкт, подобный блоку, создается с помощью пары макросов ENTER/LEAVE (см. "Возвращение скаляра" в perlcall). Такой конструкт может быть создан специально для какой-либо важной локализованной задачи или может быть использован существующий (например, границы содержащей Perl подпрограммы/блока или существующая пара для освобождения TMP). (Во втором случае накладные расходы дополнительной локализации должны быть почти незаметными). Обратите внимание, что любой XSUB автоматически заключён в пару ENTER/LEAVE.
Внутри такого псевдо-блока доступна следующая служба:
-
SAVEINT(int i) -
SAVEIV(IV i) -
SAVEI32(I32 i) -
SAVELONG(long i) -
SAVEI8(I8 i) -
SAVEI16(I16 i) -
SAVEBOOL(int i) -
SAVESTRLEN(STRLEN i) -
Эти макросы организуют восстановление значения целочисленной переменной
iв конце содержащего псевдо-блока. -
SAVESPTR(s) -
SAVEPPTR(p) -
Эти макросы организуют восстановление значений указателей
sиp.sдолжен быть указателем типа, который выдерживает преобразование вSV*и обратно, аpдолжен быть способен выдержать преобразование вchar*и обратно. -
SAVEFREESV(SV *sv) -
Счётчик ссылок на
svбудет уменьшен в конце псевдо-блока. Это похоже наsv_2mortal, поскольку это также механизм для выполнения отложенногоSvREFCNT_dec. Однако, в то время какsv_2mortalпродлевает срок жизниsvдо начала следующего оператора,SAVEFREESVпродлевает его до конца содержащей области видимости. Эти сроки жизни могут существенно различаться.Также сравните
SAVEMORTALIZESV. -
SAVEMORTALIZESV(SV *sv) -
То же, что и
SAVEFREESV, но делаетsvсмертельным (mortal) в конце текущей области видимости, а не уменьшает счётчик ссылок. Это обычно приводит к сохранениюsvдо тех пор, пока оператор, вызвавший текущую область видимости, не завершит выполнение. -
SAVEFREEOP(OP *op) -
OP *освобождается с помощью op_free() в конце псевдо-блока. -
SAVEFREEPV(p) -
Блок памяти, на который указывает
p, освобождается с помощью Safefree() в конце псевдо-блока. -
SAVECLEARSV(SV *sv) -
Очищает ячейку в текущем наборе scratchpad, которая соответствует
svв конце псевдо-блока. -
SAVEDELETE(HV *hv, char *key, I32 length) -
Ключ
keyвhvудаляется в конце псевдо-блока. Строка, на которую указываетkey, освобождается с помощью Safefree(). Если у вас есть ключ в памяти с коротким сроком жизни, соответствующая строка может быть перераспределена таким образом:SAVEDELETE(PL_defstash, savepv(tmpbuf), strlen(tmpbuf)); -
SAVEDESTRUCTOR(DESTRUCTORFUNC_NOCONTEXT_t f, void *p) -
В конце псевдо-блока вызывается функция
fс единственным аргументомp. -
SAVEDESTRUCTOR_X(DESTRUCTORFUNC_t f, void *p) -
В конце псевдо-блока вызывается функция
fс неявным аргументом контекста (если таковой имеется) иp. -
SAVESTACK_POS() -
Текущее смещение на внутреннем Perl стеке (см.
SP) восстанавливается в конце псевдо-блока.
Следующий список API содержит функции, поэтому необходимо явно передавать указатели на изменяемые данные (либо указатели C, либо Perlish GV *). Там, где вышеуказанные макросы принимают int, аналогичная функция принимает int *.
Другие макросы выше имеют реализующие их функции, но, вероятно, лучше всего использовать просто макросы, а не их или те, что ниже.
-
SV* save_scalar(GV *gv) -
Эквивалент кода Perl
local $gv. -
AV* save_ary(GV *gv) -
HV* save_hash(GV *gv) -
Аналогично
save_scalar, но локализует@gvи%gv. -
void save_item(SV *item) -
Дублирует текущее значение
SV. При выходе из текущего псевдо-блокаENTER/LEAVEзначениеSVбудет восстановлено с помощью сохранённого значения. Он не обрабатывает магию. Используйтеsave_scalarесли магические свойства затрагиваются. -
void save_list(SV **sarg, I32 maxsarg) -
Вариант
save_item, который принимает несколько аргументов через массивsargизSV*длинойmaxsarg. -
SV* save_svref(SV **sptr) -
Аналогично
save_scalar, но восстановитSV *. -
void save_aptr(AV **aptr) -
void save_hptr(HV **hptr) -
Аналогично
save_svref, но локализуетAV *иHV *.
Модуль Alias реализует локализацию основных типов в области видимости вызывающего. Те, кто заинтересован в том, как локализовать вещи в содержащей области видимости, также должны взглянуть на него.
Подпрограммы
XSUB и стек аргументов
Механизм XSUB — простой способ для Perl-программ доступа к C-подпрограммам. XSUB-процедура будет иметь стек, содержащий аргументы из Perl-программы, и способ сопоставления из Perl-структур данных с эквивалентом C.
Аргументы стека доступны через макрос ST(n), который возвращает %%%CODE_BLOCK_662%%'-й аргумент стека. Аргумент 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 (целевые), которые (как следствие) не постоянно освобождаются/создаются.
Каждый из целевых объектов создаётся только один раз (но см. "Scratchpad и рекурсия" ниже), и когда инструкция должна поместить целое число, двойное число или строку в стек, она просто устанавливает соответствующие части своего целевого объекта и помещает целевой объект в стек.
Макрос для помещения этого целевого объекта в стек — 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* в стек, который, как отмечено в "XSUBs и стек аргументов", часто должен быть "mortal". Новые макросы m(X)PUSH[iunp] упрощают это, создавая для вас новый mortal (через (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.
Стек
Остаётся вопрос о том, когда создаются SVs, являющиеся целями для команд. Ответ заключается в том, что они создаются во время компиляции текущего блока — подпрограммы или файла (для команд, находящихся вне подпрограмм). В это время создается специальный анонимный массив Perl, который называется стеком для текущего блока.
Стек хранит SVs, которые являются локальными переменными для текущего блока и являются целями команд. Предыдущая версия этого документа утверждала, что можно определить, что SV находится в стеке, посмотрев на его флаги: локальные переменные имеют SVs_PADMY установленным, а цели — SVs_PADTMP. Но это никогда не было полностью истинным. SVs_PADMY может быть установлен для переменной, которая больше не находится в любом стеке. Хотя цели имеют SVs_PADTMP установленным, оно также может быть установлено для переменных, которые никогда не находились в стеке, но тем не менее ведут себя как цели. Начиная с Perl 5.21.5, флаг SVs_PADMY больше не используется и определён как 0. SvPADMY() теперь возвращает true для всего, у чего нет SVs_PADTMP.
Соответствие между операциями и целями не является однозначным. Различные операции в дереве компиляции блока могут использовать одну и ту же цель, если это не конфликтует с ожидаемым жизненным циклом временной переменной.
Стек и рекурсия
На самом деле не совсем верно, что скомпилированный блок содержит указатель на массив стека AV. На самом деле он содержит указатель на массив AV из (изначально) одного элемента, и этот элемент — массив стека AV. Зачем нам нужен дополнительный уровень косвенности?
Ответ — рекурсия, и, возможно, потоки. Оба они могут создавать несколько указателей выполнения, направленных в одну и ту же подпрограмму. Для того, чтобы дочерняя подпрограмма не перезаписывала временные переменные родительской подпрограммы (жизненный цикл которой охватывает вызов дочерней подпрограммы), родительская и дочерняя подпрограммы должны иметь разные стеки. (И локальные переменные должны быть отдельными!)
Таким образом, каждая подпрограмма рождается с массивом стеков (длиной 1). При каждом входе в подпрограмму проверяется, что текущая глубина рекурсии не превышает длины этого массива, и если превышает, создаётся новый стек и добавляется в массив.
Цели в этом стеке — undefы, но они уже помечены правильными флагами.
Выделение памяти
Выделение
Вся память, предназначенная для использования с функциями 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.
Каждый из этих узлов представляет собой операцию, фундаментальную операцию в ядре Perl. Код, реализующий каждую операцию, можно найти в файлах pp*.c; функция, реализующая операцию с типом gvsv, — pp_gvsv, и так далее. Как показывает дерево выше, у разных операций разное количество дочерних узлов: add — бинарный оператор, как можно было ожидать, и у него две дочерние операции. Для учёта различного количества дочерних узлов существует несколько типов структуры данных op, и они связаны разными способами.
Простейший тип структуры данных op — OP: у него нет дочерних узлов. Унарные операторы, UNOPы, имеют одного потомка, и на него указывает поле op_first . У бинарных операторов (BINOPы) есть не только поле op_first, но и поле op_last . Самый сложный тип op — LISTOP, который имеет любое количество дочерних узлов. В этом случае первый дочерний узел указывается через поле op_first, а последний — через поле op_last. Дочерние узлы между ними можно найти, итеративно следуя указателю OpSIBLING от первого дочернего к последнему (но см. ниже).
Также есть и другие типы op: PMOP содержит регулярное выражение и не имеет потомков, а LOOP может или не может иметь потомков. Если поле op_children ненулевое, оно ведёт себя как LISTOP . Для усложнения ситуации, если UNOP на самом деле является оператором null после оптимизации (см. "Компиляция этап 2: распространение контекста") он всё ещё будет иметь потомков в соответствии со своим прежним типом.
Наконец, есть LOGOP, или логический оператор. Как и LISTOP, он имеет одного или более потомков, но не имеет поля op_last: вам нужно следовать указателю op_first и цепочке OpSIBLING для нахождения последнего потомка. Вместо этого у него есть поле op_other, которое сравнимо с полем op_next, описанным ниже, и представляет собой альтернативный путь выполнения. Операторы, такие как and, or и ?, — LOGOPы. Обратите внимание, что, как правило, op_other может не указывать на какого-либо из прямых дочерних узлов LOGOP.
Начиная с версии 5.21.2, в Perl, скомпилированных с экспериментальным определением -DPERL_OP_PARENT, добавляется дополнительный булевый флаг для каждой операции, op_moresib. Если он не установлен, это указывает на то, что это последняя операция в цепочке OpSIBLING. Это освобождает поле op_sibling на последнем дочернем узле для указания обратно на родительский узел. В этой сборке поле также переименовано в op_sibparent, чтобы отразить его двойную роль. Макрос OpSIBLING(o) оборачивает это специальное поведение и всегда возвращает NULL для последнего дочернего узла. В этой сборке функция op_parent(o) может использоваться для нахождения родительского узла любой операции. Поэтому для совместимости с будущими версиями вы всегда должны использовать макрос OpSIBLING(o) вместо непосредственного доступа к op_sibling.
Ещё один способ просмотреть дерево — использовать модуль компилятора backend, такой как B::Concise.
Компиляция этап 1: процедуры проверки
Дерево создаётся компилятором, когда код yacc подаёт ему конструкции, которые он распознаёт. Поскольку yacc работает сверху вниз, то и первый проход компиляции Perl работает также сверху вниз.
Что делает этот проход интересным для разработчиков Perl, так это то, что на этом проходе может быть выполнено некоторое оптимизация. Это оптимизация так называемыми «проверяющими процедурами». Соответствие между именами узлов и соответствующими проверяющими процедурами описано в opcode.pl (не забудьте запустить make regen_headers, если вы изменяете этот файл).
Проверяющая процедура вызывается, когда узел полностью построен, за исключением потока порядка выполнения. Поскольку в это время нет обратных ссылок на текущий построенный узел, можно выполнить практически любое действие над узлом верхнего уровня, включая его освобождение и/или создание новых узлов над/под ним.
Проверяющая процедура возвращает узел, который должен быть вставлен в дерево (если узел верхнего уровня не был изменён, проверяющая процедура возвращает свой аргумент).
По соглашению, проверяющие процедуры имеют имена ck_*. Обычно они вызываются из new*OP подпрограмм (или convert) (которые в свою очередь вызываются из perly.y).
Компиляция проход 1a: константное сворачивание
Сразу после вызова проверяющей процедуры возвращённый узел проверяется на выполнимость во время компиляции. Если это так (значение считается константным), оно сразу выполняется, и вместо него подставляется узел constant со «значением возврата» соответствующего поддерева. Поддерево удаляется.
Если константное сворачивание не выполнено, создаётся поток порядка выполнения.
Компиляция проход 2: распространение контекста
Когда контекст для части дерева компиляции известен, он распространяется вниз по дереву. В это время контекст может иметь 5 значений (вместо 2 для контекста во время выполнения): void, boolean, scalar, list и lvalue. В отличие от прохода 1, этот проход обрабатывается сверху вниз: контекст узла определяет контекст для его дочерних элементов.
В это время выполняются дополнительные оптимизации, зависящие от контекста. Поскольку в данный момент дерево компиляции содержит обратные ссылки (через указатели «потока»), узлы не могут быть освобождены (free()). Для того, чтобы позволить оптимизированные узлы на этом этапе, такие узлы нуллируются (null()) вместо освобождения (т.е. их тип изменяется на OP_NULL).
Компиляция проход 3: оптимизация ближайшего прохода
После создания дерева компиляции для подпрограммы (или для eval или файла) выполняется дополнительный проход по коду. Этот проход не является ни сверху вниз, ни снизу вверх, а в порядке выполнения (с дополнительными осложнениями для условных операторов). Оптимизации, выполняемые на этом этапе, подчиняются тем же ограничениям, что и на проходе 2.
Оптимизации ближайшего прохода выполняются путём вызова функции, на которую указывает глобальная переменная PL_peepp. По умолчанию, PL_peepp просто вызывает функцию, на которую указывает глобальная переменная PL_rpeepp. По умолчанию, эта функция выполняет некоторые базовые исправления и оптимизации вдоль цепочки операций порядка выполнения, и рекурсивно вызывает PL_rpeepp для каждой боковой цепочки операций (получающейся от условных операторов). Расширения могут предоставлять дополнительные оптимизации или исправления, подключаясь к этапу для каждой подпрограммы или рекурсивному этапу, подобно этому:
static peep_t prev_peepp;
static void my_peep(pTHX_ OP *o)
{
/* custom per-subroutine optimisation goes here */
prev_peepp(aTHX_ o);
/* custom per-subroutine optimisation may also go here */
}
BOOT:
prev_peepp = PL_peepp;
PL_peepp = my_peep;
static peep_t prev_rpeepp;
static void my_rpeep(pTHX_ OP *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 используется при отладке, а Perl_runops_standard используется в противном случае. Для тонкого управления выполнением дерева компиляции можно предоставить собственную функцию runops.
Вероятно, лучше скопировать одну из существующих функций runops и изменить её в соответствии с вашими потребностями. Затем в разделе BOOT вашего файла XS добавьте строку:
PL_runops = my_runops; Эта функция должна быть максимально эффективной, чтобы ваши программы работали как можно быстрее.
Временные метки области компиляции
Начиная с perl 5.14, можно подключиться к механизму лексического охвата во время компиляции с помощью Perl_blockhook_register. Это используется так:
STATIC void my_start_hook(pTHX_ int full);
STATIC BHK my_hooks;
BOOT:
BhkENTRY_set(&my_hooks, bhk_start, my_start_hook);
Perl_blockhook_register(aTHX_ &my_hooks); Это организует вызов my_start_hook в начале компиляции каждой лексической области. Доступные подключения:
-
void bhk_start(pTHX_ int full) -
Вызывается сразу после начала новой лексической области. Обратите внимание, что Perl-код, подобный
if ($x) { ... }создаёт две области: первая начинается в
(и имеетfull == 1, вторая начинается в{и имеетfull == 0. Обе заканчиваются в}, поэтому вызовыstartиpre/post_endбудут совпадать. Всё, что было помещено в стек сохранения этой подключаемой функцией, будет извлечено непосредственно перед завершением области (между подключаемыми функциямиpre_иpost_end). -
void bhk_pre_end(pTHX_ OP **o) -
Вызывается в конце лексической области, непосредственно перед развёртыванием стека. o — корень дерева optree, представляющего область; это двойной указатель, поэтому вы можете заменить OP, если нужно.
-
void bhk_post_end(pTHX_ OP **o) -
Вызывается в конце лексической области, сразу после развёртывания стека. o, как и выше. Обратите внимание, что вызовы
pre_иpost_endмогут вложены, если в стеке сохранения есть что-то, что вызывает строковое eval. -
void bhk_eval(pTHX_ OP *const o) -
Вызывается непосредственно перед началом компиляции
eval STRING,do FILE,requireилиuse, после того как eval был настроен. o — OP, который запросил eval, и обычно будетOP_ENTEREVAL,OP_DOFILEилиOP_REQUIRE.
После того, как у вас есть ваши функции подключения, вам понадобится структура BHK для их размещения. Лучше всего выделить её статически, так как нет способа её освободить, как только она зарегистрирована. Указатели функций должны быть вставлены в эту структуру с помощью макроса BhkENTRY_set, который также установит флаги, указывающие, какие записи действительны. Если вам всё-таки нужно динамически выделить вашу структуру BHK по какой-либо причине, убедитесь, что вы её обнулили перед началом.
После регистрации нет механизма для отключения этих подключаемых функций, поэтому если это необходимо, вам нужно сделать это самостоятельно. Запись в %^H — вероятно, лучший способ, так как эффект лексически ограничен; однако также возможно временно включить и выключить записи с помощью макросов BhkDISABLE и BhkENABLE. Также следует помнить, что, как правило, по крайней мере одна область будет открыта до загрузки вашего расширения, поэтому вы увидите пары pre/post_end, у которых не было соответствующей пары start.
Просмотр внутренних структур данных с помощью функций dump
Для помощи в отладке исходный файл dump.c содержит ряд функций, которые генерируют отформатированный вывод внутренних структур данных.
Наиболее часто используемой из этих функций является Perl_sv_dump; она используется для вывода SVs, AVs, HVs и CVs. Модуль Devel::Peek вызывает sv_dump для вывода отладочной информации из Perl-пространства, поэтому пользователям этого модуля уже знакома его форма.
Perl_op_dump может быть использована для вывода структуры OP или любого её производного, и генерирует вывод, аналогичный perl -Dx; на самом деле, Perl_dump_eval будет выводить основной корень оцениваемого кода точно так же, как -Dx.
Другими полезными функциями являются Perl_dump_sub, которая преобразует GV в дерево операций, и Perl_dump_packsubs, которая вызывает Perl_dump_sub для всех подпрограмм в пакете следующим образом: (К счастью, все это xsubs, поэтому нет дерева операций)
(gdb) print Perl_dump_packsubs(PL_defstash)
SUB attributes::bootstrap = (xsub 0x811fedc 0)
SUB UNIVERSAL::can = (xsub 0x811f50c 0)
SUB UNIVERSAL::isa = (xsub 0x811f304 0)
SUB UNIVERSAL::VERSION = (xsub 0x811f7ac 0)
SUB DynaLoader::boot_DynaLoader = (xsub 0x805b188 0) и Perl_dump_all, которая выводит все подпрограммы в стеке и дерево операций основного корня.
Как поддерживаются несколько интерпретаторов и параллельность
Общие сведения и 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» как «секретный» или «статичный»). Все остальные функции начинаются с "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' для прототипа, 'a' для аргумента или 'd' для декларации, поэтому у нас есть pTHX, aTHX и dTHX, и их варианты.
Когда Perl скомпилирован без опций, устанавливающих MULTIPLICITY, нет первого аргумента, содержащего контекст интерпретатора. Завершающая нижняя черта в макросе pTHX_ указывает, что расширение макроса требует запятой после аргумента контекста, поскольку за ним следуют другие аргументы. Если MULTIPLICITY не определён, pTHX_ будет проигнорирован, и подпрограмма не будет прототипирована для принятия дополнительного аргумента. Форма макроса без заключительной нижней черты используется, когда нет дополнительных явных аргументов.
Когда одна ядровая функция вызывает другую, она должна передать контекст. Это обычно скрывается с помощью макросов. Рассмотрим 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.
Однако это не работает так чисто для функций с переменным числом аргументов, поскольку макросы подразумевают, что число аргументов известно заранее. Вместо этого нам нужно либо полностью их написать, передавая 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.
Есть три способа сделать это. Во-первых, лёгкий, но неэффективный способ, который также является по умолчанию для сохранения обратной совместимости с расширениями: всякий раз, когда включается 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; объявления в начале каждой функции, которая будет вызывать Perl API. (Вы узнаете, какие функции нуждаются в этом, потому что компилятор 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 из нескольких потоков?
Если вы создаёте интерпретаторы в одном потоке, а затем вызываете их в другом, вам нужно убедиться, что собственный слот локального хранилища потоков (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 и других автоматически сгенерированных файлов.
Форматированный вывод IV, UV и NV
Если вы выводите IV, UV или NV вместо кодов форматирования stdio(3), таких как %d, %ld, %f, используйте следующие макросы для обеспечения переносимости.
IVdf IV in decimal
UVuf UV in decimal
UVof UV in octal
UVxf UV in hexadecimal
NVef NV %e-like
NVff NV %f-like
NVgf NV %g-like Они будут обрабатывать 64-битные целые числа и длинные двойные числа. Например:
printf("IV is %" IVdf "\n", iv); IVdf будет расширяться до правильного формата для IV. Обратите внимание, что пробелы вокруг формата необходимы в случае компиляции кода с C++, чтобы сохранить соответствие стандарту.
Обратите внимание, что существует несколько типов "длинных двойных чисел": Perl будет использовать тот, который имеет компилятор.
Если вы выводите адреса указателей, используйте %p или UVxf в сочетании с PTR2UV().
Форматированный вывод SVs
Содержимое SVs можно выводить с помощью формата SVf, например:
Perl_croak(aTHX_ "This croaked because: %" SVf "\n", SVfARG(err_msg)) где err_msg — SV.
Не все типы скаляров могут быть выведены. Простые значения определённо могут: IV, UV, NV или PV. Кроме того, если SV является ссылкой на некоторое значение, оно будет разыменовано и выведено значение, или будет выведена информация о типе этого значения и его адресе. Результаты вывода любого другого типа SV не определены и, скорее всего, приведут к аварийному завершению работы интерпретатора. NV выводятся с использованием формата, похожим на %g.
Обратите внимание, что пробелы вокруг SVf необходимы в случае компиляции кода с C++, чтобы сохранить соответствие стандарту.
Обратите внимание, что любой файловый дескриптор, вывод в который выполняется в UTF-8, должен ожидать UTF-8, чтобы получить правильные результаты и избежать предупреждений о широких символах. Один из способов сделать это для типичных файловых дескрипторов — вызвать perl с параметром -C>. (См. "-C [number/list]" в perlrun.
Вы можете использовать это для конкатенации двух скаляров:
SV *var1 = get_sv("var1", GV_ADD);
SV *var2 = get_sv("var2", GV_ADD);
SV *var3 = newSVpvf("var1=%" SVf " and var2=%" SVf,
SVfARG(var1), SVfARG(var2)); Форматированный вывод строк
Если вам нужно вывести только байты в строке с завершающим нулём в 7-битном формате, вы можете использовать %s (предполагая, что они все действительно только 7-битные). Но если есть возможность, что значение будет закодировано в UTF-8 или содержит байты выше 0x7F (и поэтому 8-битные), вам следует вместо этого использовать формат UTF8f. В качестве параметра используйте макрос UTF8fARG().
chr * msg;
/* U+2018: \xE2\x80\x98 LEFT SINGLE QUOTATION MARK
U+2019: \xE2\x80\x99 RIGHT SINGLE QUOTATION MARK */
if (can_utf8)
msg = "\xE2\x80\x98Uses fancy quotes\xE2\x80\x99";
else
msg = "'Uses simple quotes'";
Perl_croak(aTHX_ "The message is: %" UTF8f "\n",
UTF8fARG(can_utf8, strlen(msg), msg)); Первый параметр UTF8fARG — булево значение: 1, если строка в UTF-8; 0, если строка в кодировке байтов по умолчанию (Latin1). Второй параметр — число байтов в строке, которые нужно вывести. Третий и последний параметр — указатель на первый байт в строке.
Обратите внимание, что любой файловый дескриптор, вывод в который выполняется в UTF-8, должен ожидать UTF-8, чтобы получить правильные результаты и избежать предупреждений о широких символах. Один из способов сделать это для типичных файловых дескрипторов — вызвать perl с параметром -C>. (См. "-C [number/list]" в perlrun.
Форматированный вывод Size_t и SSize_t
Наиболее общий способ сделать это — привести их к типу UV или IV и напечатать, как в предыдущем разделе.
Но если вы используете PerlIO_printf(), для экономии набора и уменьшения визуального мусора можно воспользоваться модификатором длины %z (для размера):
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 будут иметь установленный старший бит, поэтому вы можете проверить, нужно ли сделать что-то особенное с этим символом, например, так (UTF8_IS_INVARIANT() — это макрос, который проверяет, закодирован ли байт как один байт даже в UTF-8):
U8 *utf; /* Initialize this to point to the beginning of the
sequence to convert */
U8 *utf_end; /* Initialize this to 1 beyond the end of the sequence
pointed to by 'utf' */
UV uv; /* Returned code point; note: a UV, not a U8, not a
char */
STRLEN len; /* Returned length of character in bytes */
if (!UTF8_IS_INVARIANT(*utf))
/* Must treat this as UTF-8 */
uv = utf8_to_uvchr_buf(utf, utf_end, &len);
else
/* OK to treat this character as a byte */
uv = *utf; В этом примере вы также можете увидеть, что мы используем utf8_to_uvchr_buf для получения значения символа; обратная функция uvchr_to_utf8 доступна для помещения UV в UTF-8:
if (!UVCHR_IS_INVARIANT(uv))
/* Must treat this as UTF8 */
utf8 = uvchr_to_utf8(utf8, uv);
else
/* OK to treat this character as a byte */
*utf8++ = uv; Вы обязательно должны преобразовывать символы в UV с помощью вышеуказанных функций, если вы когда-либо окажетесь в ситуации, когда вам нужно сопоставить символы UTF-8 и не-UTF-8. В этом случае вы не можете пропустить символы UTF-8. Если вы это сделаете, вы потеряете возможность сопоставить символы с высоким битом, не являющиеся UTF-8; например, если ваша строка UTF-8 содержит v196.172, и вы пропустите этот символ, вы никогда не сможете сопоставить chr(200) в строке, не являющейся UTF-8. Поэтому не делайте этого!
(Обратите внимание, что в приведенных выше примерах нам не нужно проверять неизменяемые символы. Функции работают с любым правильно сформированным вводом UTF-8. Просто быстрее избежать накладных расходов на функции, когда они не нужны.)
Как Perl хранит строки UTF-8?
В настоящее время Perl обрабатывает строки UTF-8 и не-UTF-8 несколько по-разному. Флаг в SV, SVf_UTF8, указывает, что строка закодирована внутри как UTF-8. Без него значение байта — это числовое значение кода, и наоборот. Этот флаг имеет смысл только в том случае, если SV является SvPOK или сразу после строкового форматирования через SvPV или аналогичный макрос. Вы можете проверить и изменить этот флаг с помощью следующих макросов:
SvUTF8(sv)
SvUTF8_on(sv)
SvUTF8_off(sv) Этот флаг оказывает существенное влияние на обработку строки Perl: если данные UTF-8 не правильно различаются, регулярные выражения, length, substr и другие операции со строками дадут нежелательные (неправильные) результаты.
Проблема возникает, когда у вас есть, например, строка, которая не помечена как UTF-8 и содержит последовательность байтов, которая может быть UTF-8, — особенно при объединении строк, не являющихся UTF-8 и UTF-8.
Никогда не забывайте, что флаг SVf_UTF8 отделен от значения PV; вам нужно убедиться, что вы случайно его не сбросили во время работы с SV. Более конкретно, вы не можете ожидать этого:
SV *sv;
SV *nsv;
STRLEN len;
char *p;
p = SvPV(sv, len);
frobnicate(p);
nsv = newSVpvn(p, len); Строка char* не содержит всей информации, и вы не можете скопировать или восстановить SV, просто скопировав значение строки. Проверьте, установлен ли во флаге старого SV флаг UTF8 (после вызова 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, должны быть показаны, а не символ, который они представляют. Но это pragma следует использовать только для отладки и, возможно, низкоуровневого тестирования на уровне байтов. Поэтому большинство кодов XS не должны этим заниматься, но различные части ядра Perl должны его поддерживать.
И это еще не вся история. Начиная с Perl v5.12, строки, которые не закодированы в UTF-8, также могут обрабатываться как Unicode при различных условиях (см. "Правила ASCII против правил Unicode" в perlunicode). Это проблема только для символов, чьи порядковые номера находятся между 128 и 255, и их поведение различается в соответствии с правилами ASCII и Unicode способами, которые важны для вашего кода (см. "Ошибка Unicode" в 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 — всегда в паре с поисками в SvUTF8 — может быть безопасной и (незначительно) более эффективной.
СОВЕТ ПО ТЕСТИРОВАНИЮ: Используйте функции 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. -
При записи кодовой точки символа в строку UTF-8 всегда используйте
uvchr_to_utf8, за исключением случаевUVCHR_IS_INVARIANT(uv)), в которых вы можете использовать*s = uv. -
Смешивание строк UTF-8 и не-UTF-8 сложно. Используйте
bytes_to_utf8для получения новой строки, закодированной в UTF-8, и затем объедините их.
Пользовательские операторы
Поддержка пользовательских операторов — это экспериментальная функция, которая позволяет определять собственные операторы. Это в основном для возможности создания интерпретаторов других языков в ядре Perl, но это также позволяет оптимизировать код через создание "макро-операторов" (операторов, которые выполняют функции нескольких операторов, которые обычно выполняются вместе, таких как gvsv, gvsv, add).
Эта функция реализуется как новый тип оператора, OP_CUSTOM. Ядро Perl не "знает" ничего особенного об этом типе оператора и поэтому не будет участвовать в каких-либо оптимизациях. Это также означает, что вы можете определить свои пользовательские операторы, чтобы они были любыми операторными структурами — унарными, бинарными, списковыми и т. д. — которые вам нужны.
Важно знать, чего не сделают пользовательские операторы для вас. Они не позволят вам добавлять новый синтаксис в Perl напрямую. Они даже не позволят добавить новые ключевые слова напрямую. На самом деле, они не изменят способ компиляции Perl программы вообще. Вам нужно сделать эти изменения самостоятельно после того, как Perl скомпилирует программу. Вы делаете это, либо манипулируя деревом операторов с помощью блока CHECK и модуля B::Generate, либо добавляя пользовательский оптимизатор с модулем optimize.
Когда вы это делаете, вы заменяете обычные операторы Perl пользовательскими операторами, создавая операторы с типом OP_CUSTOM и op_ppaddr вашей собственной функции PP. Это должно быть определено в коде XS и должно выглядеть как операторы PP в pp_*.c. Вы несёте ответственность за то, чтобы ваш оператор принимал нужное количество значений из стека, и за добавление меток стека при необходимости.
Вы также должны "зарегистрировать" свой оператор в интерпретаторе Perl, чтобы он мог генерировать осмысленные сообщения об ошибках и предупреждениях. Поскольку возможно иметь несколько пользовательских операторов внутри одного "логического" типа операторов OP_CUSTOM, Perl использует значение o->op_ppaddr для определения того, с каким пользовательским оператором он имеет дело. Вы должны создать структуру XOP для каждого ppaddr, который вы используете, установить свойства пользовательского оператора с помощью XopENTRY_set и зарегистрировать структуру по отношению к ppaddr с помощью Perl_custom_op_register. Тривиальный пример может выглядеть так:
static XOP my_xop;
static OP *my_pp(pTHX);
BOOT:
XopENTRY_set(&my_xop, xop_name, "myxop");
XopENTRY_set(&my_xop, xop_desc, "Useless custom op");
Perl_custom_op_register(aTHX_ my_pp, &my_xop); Доступные поля в структуре:
- xop_name
-
Короткое имя для вашего оператора. Это будет включено в некоторые сообщения об ошибках и также будет возвращено как
$op->nameмодулем B, поэтому оно будет отображаться в выводе модуля, например, B::Concise. - xop_desc
-
Короткое описание функции оператора.
- xop_class
-
Какой из различных структур
*OPиспользует этот оператор. Это должно быть одно из константOA_*из op.h, а именно- OA_BASEOP
- OA_UNOP
- OA_BINOP
- OA_LOGOP
- OA_LISTOP
- OA_PMOP
- OA_SVOP
- OA_PADOP
- OA_PVOP_OR_SVOP
-
Это следует понимать как '
PVOP' только._OR_SVOP— потому что только ядраPVOP,OP_TRANS, иногда могут бытьSVOP. - OA_LOOP
- OA_COP
Другие константы
OA_*не должны использоваться. - xop_peep
-
Этот член имеет тип
Perl_cpeep_t, что расширяется доvoid (*Perl_cpeep_t)(aTHX_ OP *o, OP *oldop). Если он установлен, эта функция будет вызвана изPerl_rpeepпри встрече операторов этого типа оптимизатором peephole. o — это OP, который нужно оптимизировать; oldop — предыдущий оптимизированный OP, чьёop_nextуказывает на o.
B::Generate напрямую поддерживает создание пользовательских операторов по имени.
Стек
Вышеупомянутые описания иногда ссылаются на "стек", но на самом деле в интерпретаторе Perl существует множество подобных стекам структур данных. Когда "стек" не указан иначе, он обычно относится к стеку значений.
Различные стеки имеют разные цели и работают немного по-разному. Их отличия указаны ниже.
Стек значений
Этот стек хранит значения, на которых работает обычный код Perl, обычно промежуточные значения выражений в рамках оператора. Сам стек формируется из массива указателей SV.
Основание этого стека указывается переменной интерпретатора PL_stack_base, типа SV **.
Вершина стека — PL_stack_sp, и указывает на последний добавленный элемент.
Элементы добавляются в стек с помощью макроса PUSHs() или его вариантов, описанных выше; XPUSHs(), mPUSHs(), mXPUSHs() и типизированные версии. Внимательно обратите внимание, что не-X версии этих макросов не проверяют размер стека и предполагают, что он достаточно большой. Эти макросы должны быть сопряжены с соответствующей проверкой размера стека, например, с макросом EXTEND, чтобы убедиться, что он достаточно велик. Например,
EXTEND(SP, 4);
mPUSHi(10);
mPUSHi(20);
mPUSHi(30);
mPUSHi(40);Это немного производительнее, чем выполнение четырёх отдельных проверок в четырёх отдельных mXPUSHi() вызовах.
В качестве дальнейшей оптимизации производительности различные PUSH макросы работают с локальной переменной SP, а не с интерпретаторской глобальной переменной PL_stack_sp. Эта переменная объявляется макросом dSP — хотя обычно она подразумевается XSUB и подобными функциями, поэтому редко нужно рассматривать её напрямую. После объявления, макросы PUSH будут работать только с этой локальной переменной, поэтому перед вызовом других функций ядра Perl необходимо использовать макрос PUTBACK, чтобы вернуть значение из локальной переменной SP обратно в интерпретаторскую переменную. Аналогично, после вызова функции ядра Perl, которая могла сдвигать стек или добавлять/удалять значения из него, необходимо использовать макрос 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 типа двойного указателя, называемую 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, которые представляют базовый объем (как при push pp_enter) и блок сортировки. Тип определяет, какие части объединения контекста допустимы.
Основное разделение в структуре контекста — между областью подстановки (CXt_SUBST) и областями блоков, которые являются всем остальным. Первая используется только во время выполнения s///e, и далее здесь обсуждаться не будет.
Все типы областей блоков имеют общую базу, соответствующую CXt_BLOCK. Она хранит старые значения различных переменных, связанных с областью, таких как PL_curpm, а также информацию о текущей области, например, gimme. При выходе из области старые переменные восстанавливаются.
Конкретные типы областей блоков хранят дополнительную информацию для каждого типа. Например, CXt_SUB хранит текущий исполняемый CV, а различные типы циклов for могут содержать исходную переменную цикла SV. При выходе из области данные по типу обрабатываются; например, счётчик ссылок CV уменьшается, а исходная переменная цикла восстанавливается.
Макрос cxstack возвращает основу текущей стека контекстов, а cxstack_ix — индекс текущей рамки в этом стеке.
Фактически, стек контекстов является частью системы стека-из-стеков; всякий раз, когда выполняется что-то необычное, например, вызов обработчика DESTROY или связи, то новый стек помещается, а затем извлекается в конце.
Обратите внимание, что описанный здесь API значительно изменился в Perl 5.24. До этого использовались большие макросы, такие как PUSHBLOCK и POPSUB; в 5.24 они были заменены описанными ниже встроенными статическими функциями. Кроме того, порядок и детали работы этих макросов/функций изменились во многих отношениях, часто незаметно. В частности, они не обрабатывали сохранение позиций стека сохранений и стека временных переменных и требовали дополнительных ENTER, SAVETMPS и LEAVE по сравнению с новыми функциями. Макросы старого стиля более подробно не описываются.
Добавление контекстов
Для добавления нового контекста используются две основные функции: cx = cx_pushblock(), которая добавляет новый базовый блок контекста и возвращает его адрес, и семейство подобных функций с названиями, например, cx_pushsub(cx), которые заполняют дополнительные поля, зависящие от типа, в структуре cx. Обратите внимание, что CXt_NULL и CXt_BLOCK не имеют собственных функций добавления, так как они не хранят никаких данных, помимо тех, которые добавляет cx_pushblock.
Поля структуры контекста и аргументы функций cx_* могут меняться между выпусками Perl, отражая то, что удобно или эффективно для этого выпуска.
Типичный пример добавления контекста можно найти в pp_entersub; следующее — упрощённый и сокращённый пример вызова не-XS, вместе с комментариями, примерно показывающими, что делает каждая функция.
dMARK;
U8 gimme = GIMME_V;
bool hasargs = cBOOL(PL_op->op_flags & OPf_STACKED);
OP *retop = PL_op->op_next;
I32 old_ss_ix = PL_savestack_ix;
CV *cv = ....;
/* ... make mortal copies of stack args which are PADTMPs here ... */
/* ... do any additional savestack pushes here ... */
/* Now push a new context entry of type 'CXt_SUB'; initially just
* doing the actions common to all block types: */
cx = cx_pushblock(CXt_SUB, gimme, MARK, old_ss_ix);
/* this does (approximately):
CXINC; /* cxstack_ix++ (grow if necessary) */
cx = CX_CUR(); /* and get the address of new frame */
cx->cx_type = CXt_SUB;
cx->blk_gimme = gimme;
cx->blk_oldsp = MARK - PL_stack_base;
cx->blk_oldsaveix = old_ss_ix;
cx->blk_oldcop = PL_curcop;
cx->blk_oldmarksp = PL_markstack_ptr - PL_markstack;
cx->blk_oldscopesp = PL_scopestack_ix;
cx->blk_oldpm = PL_curpm;
cx->blk_old_tmpsfloor = PL_tmps_floor;
PL_tmps_floor = PL_tmps_ix;
*/
/* then update the new context frame with subroutine-specific info,
* such as the CV about to be executed: */
cx_pushsub(cx, cv, retop, hasargs);
/* this does (approximately):
cx->blk_sub.cv = cv;
cx->blk_sub.olddepth = CvDEPTH(cv);
cx->blk_sub.prevcomppad = PL_comppad;
cx->cx_type |= (hasargs) ? CXp_HASARGS : 0;
cx->blk_sub.retop = retop;
SvREFCNT_inc_simple_void_NN(cv);
*/ Обратите внимание, что cx_pushblock() создаёт два новых уровня: для стека аргументов (до MARK) и стека временных переменных (до PL_tmps_ix). Во время выполнения на этом уровне области все nextstate (среди прочих) будут сбрасывать уровни стека аргументов и временных переменных до этих уровней. Обратите внимание, что поскольку cx_pushblock использует текущее значение PL_tmps_ix, а не передаёт его в качестве аргумента, это определяет, когда нужно вызывать cx_pushblock. В частности, все новые временные переменные, которые должны быть освобождены только при выходе из области (а не на следующем nextstate), должны быть созданы в первую очередь.
Большинство вызывающих функций cx_pushblock просто устанавливают новый нижний уровень стека аргументов на вершину предыдущей рамки, но для CXt_LOOP_LIST он хранит итерируемые элементы в стеке, и, следовательно, устанавливает blk_oldsp на вершину этих элементов. Обратите внимание, что, вопреки своему названию, blk_oldsp не всегда представляет значение, которое нужно восстановить PL_stack_sp при выходе из области.
Обратите внимание на раннее получение PL_savestack_ix до old_ss_ix, которое позднее передаётся в качестве аргумента в cx_pushblock. В случае pp_entersub это происходит потому, что, хотя большинство значений, требующих сохранения, хранятся в полях структуры контекста, дополнительное значение необходимо сохранять только при запуске отладчика, и нет смысла увеличивать структуру для этого редкого случая. Вместо этого оно сохраняется в стеке сохранений. Поскольку это значение рассчитывается и сохраняется до того, как контекст будет добавлен, необходимо передать старое значение PL_savestack_ix в cx_pushblock, чтобы гарантировать освобождение сохранённого значения при выходе из области. Для большинства пользователей cx_pushblock, где ничего не нужно добавлять в стек сохранений, PL_savestack_ix просто передаётся непосредственно в качестве аргумента в cx_pushblock.
Обратите внимание, что где это возможно, значения должны сохраняться в структуре контекста, а не в стеке сохранений; это намного быстрее.
Обычно cx_pushblock должен сразу же следовать за соответствующим cx_pushfoo, без чего-либо между ними; это потому, что, если код между ними может завершиться (например, предупреждение повысилось до фатального), то код обработки возвращения стека контекстов в dounwind увидит (в примере выше) рамку контекста CXt_SUB, но без всех полей, специфичных для подпрограммы, и быстро последует крах.
В тех случаях, когда два должны быть разделены, вначале установите тип на CXt_NULL или CXt_BLOCK, а затем измените его на CXt_foo, выполняя cx_pushfoo. Именно это делает pp_enteriter, как только определяется тип цикла, который добавляется.
Удаление контекстов
Контексты удаляются с помощью cx_popsub() и т. д. и cx_popblock(). Однако, в отличие от cx_pushblock, ни одна из этих функций не уменьшает текущий индекс стека контекстов; это делается отдельно с помощью CX_POP().
Существует два основных способа удаления контекстов. Во время нормального выполнения, когда области выходят, функции, такие как pp_leave, pp_leaveloop и pp_leavesub, обрабатывают и удаляют только один контекст с помощью cx_popfoo и cx_popblock. С другой стороны, такие вещи, как pp_return и next, могут потребовать удаления нескольких областей, пока не будет найден контекст подпрограммы или цикла, а исключения (например, die) должны удалять контексты до тех пор, пока не будет найден контекст eval. Оба этих действия выполняются с помощью dounwind(), которая способна обрабатывать и удалять все контексты, расположенные выше целевого.
Вот типичный пример удаления контекстов, как он используется в pp_leavesub (несколько упрощён):
U8 gimme;
PERL_CONTEXT *cx;
SV **oldsp;
OP *retop;
cx = CX_CUR();
gimme = cx->blk_gimme;
oldsp = PL_stack_base + cx->blk_oldsp; /* last arg of previous frame */
if (gimme == G_VOID)
PL_stack_sp = oldsp;
else
leave_adjust_stacks(oldsp, oldsp, gimme, 0);
CX_LEAVE_SCOPE(cx);
cx_popsub(cx);
cx_popblock(cx);
retop = cx->blk_sub.retop;
CX_POP(cx);
return retop; Шаги выше расположены в очень определённом порядке, предназначенном для обратного порядка, в котором был добавлен контекст. Первое, что нужно сделать, — скопировать и/или защитить любые возвращаемые аргументы и освободить все временные переменные в текущей области. Выходы из области, такие как подпрограмма rvalue, обычно возвращают смертную копию возвращаемых аргументов (в отличие от подпрограмм lvalue). Важно сделать эту копию перед удалением элементов из стека сохранений или восстановлением переменных, в противном случае могут произойти такие неприятные вещи, как следующие:
sub f { my $x =...; $x } # $x freed before we get to copy it
sub f { /(...)/; $1 } # PL_curpm restored before $1 copied Хотя мы хотим освободить все временные переменные одновременно, мы должны быть осторожны, чтобы не освободить временные переменные, которые поддерживают возвращаемые аргументы; ни освобождать временные переменные, которые мы только что создали, копируя возвращаемые аргументы. К счастью, leave_adjust_stacks() способен создавать смертные копии возвращаемых аргументов, сдвигать аргументы по стеку и обрабатывать только те записи в стеке временных переменных, которые безопасно обработать.
В контексте void не возвращаются аргументы, поэтому более эффективно пропустить вызов leave_adjust_stacks(). Также в контексте void оператор nextstate скорее всего будет вызван вскоре после этого, который выполнит FREETMPS, поэтому нет необходимости делать это тоже.
Следующим шагом является удаление элементов из стека сохранений: CX_LEAVE_SCOPE(cx) просто определён как LEAVE_SCOPE(cx->blk_oldsaveix). Обратите внимание, что во время удаления возможно, что Perl вызовет деструкторы, вызовет STORE для отмены локализации привязанных переменных и так далее. Любой из них может завершиться неудачно или вызвать exit(). В этом случае вызовется dounwind(), и текущая рамка стека контекстов будет обработана повторно. Таким образом, крайне важно, чтобы все шаги удаления контекста выполнялись таким образом, чтобы поддерживать возможность повторного входа. Другой вариант — уменьшить cxstack_ix до обработки рамки, приведёт к утечкам и подобным ошибкам, если что-то завершится неудачно или перепишет текущую рамку.
CX_LEAVE_SCOPE сам по себе безопасно повторно вхожден: если только часть элементов стека сохранений удалена до завершения выполнения и получения ошибки в eval, то CX_LEAVE_SCOPE в dounwind или pp_leaveeval будут продолжены с того места, где остановился первый.
Следующим шагом является обработка контекста, специфичная для типа; в этом случае cx_popsub. Часть этой обработки выглядит так:
cv = cx->blk_sub.cv;
CvDEPTH(cv) = cx->blk_sub.olddepth;
cx->blk_sub.cv = NULL;
SvREFCNT_dec(cv); где обрабатывается только что исполненный CV. Обратите внимание, что перед уменьшением счётчика ссылок CV поле blk_sub.cv обнуляется. Это означает, что если произойдёт повторный вход, CV не будет освобождён дважды. Это также означает, что вы не можете полагаться на то, что такие специфичные для типа поля будут иметь полезные значения после возврата из cx_popfoo.
Далее, cx_popblock восстанавливает все различные переменные интерпретатора до их предыдущих значений или предыдущих максимальных значений; оно расширяется до:
PL_markstack_ptr = PL_markstack + cx->blk_oldmarksp;
PL_scopestack_ix = cx->blk_oldscopesp;
PL_curpm = cx->blk_oldpm;
PL_curcop = cx->blk_oldcop;
PL_tmps_floor = cx->blk_old_tmpsfloor; Обратите внимание, что он не восстанавливает PL_stack_sp; как упоминалось ранее, какое значение восстанавливать, зависит от типа контекста (в частности, for (list) {}) и возвращаемых аргументов (если таковые имеются); и это уже будет отсортировано ранее в leave_adjust_stacks().
Наконец, указатель стека контекстов фактически уменьшается на CX_POP(cx). После этого момента возможно, что текущая рамка контекста будет перезаписана другими добавляемыми контекстами. Хотя такие вещи, как связи и DESTROY, должны работать внутри нового стека контекстов, лучше не полагаться на это. Действительно, в отладочных сборках CX_POP(cx) преднамеренно устанавливает cx в null, чтобы обнаружить код, который всё ещё полагается на значения полей в этой рамке контекста. Обратите внимание в примере pp_leavesub() выше, что мы получаем blk_sub.retop до вызова CX_POP.
Повторное выполнение контекстов
Наконец, есть cx_topblock(cx), который действует как супер-nextstate по отношению к сбросу различных переменных до их базовых значений. Он используется в таких местах, как pp_next, pp_redo и pp_goto, где вместо выхода из области мы хотим переинициализировать область. Помимо сброса PL_stack_sp подобно nextstate, он также сбрасывает PL_markstack_ptr, PL_scopestack_ix и PL_curpm. Обратите внимание, что он не выполняет FREETMPS.
Выделение операторов на основе кусков
Примечание: этот раздел описывает непубличный внутренний API, который может быть изменён без предварительного уведомления.
Внутренние механизмы обработки ошибок Perl реализуют die (и его внутренние аналоги) с помощью longjmp. Если это произойдет во время лексического анализа, синтаксического анализа или компиляции, мы должны гарантировать, что все выделенные в рамках процесса компиляции операнды будут освобождены. (Более старые версии Perl не адекватно обрабатывали эту ситуацию: при неудачном парсинге они утекали операнды, хранившиеся в C auto переменных и нигде больше не привязанные.)
Для обработки этой ситуации Perl использует блоки операндов, которые прикреплены к текущему компилируемому CV. Блок — это фрагмент выделенной памяти. Новые операнды выделяются как области блока. Если блок заполнится, создаётся новый (и он связывается с предыдущим). При возникновении ошибки и освобождении CV все оставшиеся операнды освобождаются.
Каждый операнд предваряется двумя указателями: один указывает на следующий операнд в блоке, а другой — на блок, которому он принадлежит. Указатель на следующий операнд необходим, чтобы Perl мог итерироваться по блоку и освободить все его операнды. (Структуры операндов имеют различный размер, поэтому операнды блока не могут просто рассматриваться как плотная последовательность.) Указатель на блок необходим для доступа к счетчику ссылок на блок: когда освобождается последний операнд в блоке, сам блок освобождается.
Блокировщик операндов помещает операнды в конец блока в первую очередь. Это будет склонять к выделению листьев дерева операндов в первую очередь, и, следовательно, расположение, надеемся, будет дружественным к кэшу. Кроме того, это означает, что нет необходимости хранить размер блока (см. ниже, почему блоки имеют разный размер), поскольку Perl может следовать указателям, чтобы найти последний операнд.
По-видимому, можно полностью исключить счетчики ссылок на блоки, сделав все операнды неявным образом прикреплёнными к PL_compcv при выделении и освобождаемыми при освобождении CV. Это также позволит op_free пропустить FreeOp совсем, и, следовательно, освобождать операнды быстрее. Но это не работает в тех случаях, когда операнды должны выжить за пределами своих CV, например, при повторной оценке.
CV также должен иметь счетчик ссылок на блок. Иногда первый созданный операнд сразу освобождается. Если счетчик ссылок блока достигнет 0, то он будет освобождён, хотя CV всё ещё указывает на него.
CV используют флаг CVf_SLABBED для обозначения того, что CV имеет счетчик ссылок на блок. Когда этот флаг установлен, к блоку можно получить доступ через CvSTART когда CvROOT не установлен, или вычитав два указателя (2*sizeof(I32 *)) из CvROOT когда он установлен. Альтернативой этому подходу, заключающемуся в протаскивании блока в CvSTART во время компиляции, было бы увеличение структуры xpvcv на ещё один указатель. Но это сделало бы все CV больше, даже несмотря на то, что освобождение операндов, основанное на блоках, обычно выгодно только для программ, которые активно используют строковый eval.
Когда установлен флаг CVf_SLABBED, CV берёт на себя ответственность за освобождение блока. Если CvROOT не установлен, когда CV освобождается или отменяется, предполагается, что произошла ошибка компиляции, поэтому блок операндов обходится и все операнды освобождаются.
В обычных обстоятельствах CV забывает о своём блоке (уменьшая счетчик ссылок), когда корень прикрепляется. Таким образом, подсчёт ссылок на блоки, происходящий при освобождении операндов, позаботится об освобождении блока. В некоторых случаях CV получает указание забыть о блоке (cv_forget_slab) именно для того, чтобы операнды могли выжить после того, как CV будет уничтожен.
Забывание блока при прикреплении корня не строго необходимо, но это предотвращает потенциальные проблемы с 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–2021 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.36.0/perlguts