perlguts
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ОПИСАНИЕ
- Переменные
- Типы данных
- Что такое "IV"?
- Работа с SVs
- Смещения
- Что на самом деле хранится в SV?
- Работа с AVs
- Работа с HVs
- Расширения API хешей
- AVs, HVs и неопределённые значения
- Ссылки
- Благословенные ссылки и объекты классов
- Создание новых переменных
- Счётчики ссылок и смертность
- Стелли и глобы
- Дескрипторы ввода/вывода
- SV с двойным типом
- Только для чтения значения
- Копирование при записи
- Магические переменные
- Назначение магических свойств
- Магические виртуальные таблицы
- Поиск магических свойств
- Понимание магии связанных хешей и массивов
- Локализация изменений
- Подпрограммы
- Выделение памяти
- PerlIO
- Компилируемый код
- Изучение внутренних структур данных с функциями dump
- Поддержка множественных интерпретаторов и параллельности
- Внутренние функции
- Форматированный вывод IV, UV и NV
- Форматированный вывод SV
- Форматированный вывод строк
- Форматированный вывод Size_t и SSize_t
- Форматированный вывод Ptrdiff_t, intmax_t, short и других специальных размеров
- Указатель на целое число и целое число на указатель
- Обработка исключений
- Документация источника
- Обратная совместимость
- Поддержка Юникода
- Пользовательские операторы
- Стек
- Динамический диапазон и стек контекстов
- Выделение операторов на основе блоков
- АВТОРЫ
- СМОТРИТЕ ТАКЖЕ
НАЗВАНИЕ
perlguts - Введение в Perl API
ОПИСАНИЕ
Этот документ пытается описать, как использовать Perl API, а также предоставить некоторую информацию об основных принципах работы ядра Perl. Он далёк от полного и, вероятно, содержит много ошибок. Пожалуйста, направляйте любые вопросы или комментарии автору ниже.
Переменные
Типы данных
Perl использует три определения типов, которые обрабатывают три основных типа данных Perl:
SV Scalar Value
AV Array Value
HV Hash Value Для каждого определения типа существуют специфические процедуры, которые манипулируют различными типами данных.
Что такое "IV"?
Perl использует специальное определение типа IV, которое является простым типом подписанного целого числа, гарантированно достаточным для хранения указателя (а также целого числа). Кроме того, есть UV, который просто является беззнаковым IV.
Perl также использует несколько специальных определений типов для объявления переменных, хранящих целые числа (по крайней мере) заданного размера. Используйте I8, I16, I32 и I64 для объявления переменной со знаком, которая имеет не меньше битов, чем число в её имени. Все они соответствуют близкому к заданному числу битов типу C, но не меньше его. Например, на многих платформах short имеет длину 16 бит, и в таком случае I16 будет соответствовать типу short. Но на платформах, где short не ровно 16 бит, Perl будет использовать наименьший тип, который содержит 16 бит или больше.
U8, U16, U32 и U64 предназначены для объявления соответствующих беззнаковых целых типов.
Если платформа не поддерживает 64-битные целые числа, то I64 и U64 будут неопределёнными. Используйте IV и UV для объявления наибольшего практичного, и "WIDEST_UTYPE" in perlapi для абсолютного максимума беззнакового, но который может быть не применимым во всех обстоятельствах.
Числовую константу можно указать с помощью "INT16_C" в perlapi, "UINTMAX_C" в perlapi и аналогичных.
Работа с SVs
Объект SV можно создать и загрузить одной командой. Существует пять типов загружаемых значений: целое число (IV), беззнаковое целое число (UV), число с плавающей точкой (NV), строка (PV) и другой скаляр (SV). («PV» означает «Указатель значения». Возможно, это неудачное название, так как описывается, что он указывает только на строки. Однако он может указывать и на другие вещи. Например, он может указывать на массив UV. Но использование его для нестроковых значений требует осторожности, так как основное предположение многих внутренних механизмов заключается в том, что PV предназначен только для строк. Часто, например, автоматически добавляется конечный NUL. Использование для нестроковых данных документировано только в этом абзаце.)
Семь процедур:
SV* newSViv(IV);
SV* newSVuv(UV);
SV* newSVnv(double);
SV* newSVpv(const char*, STRLEN);
SV* newSVpvn(const char*, STRLEN);
SV* newSVpvf(const char*, ...);
SV* newSVsv(SV*); STRLEN — это целочисленный тип (Size_t, обычно определяемый как size_t в config.h), гарантированно достаточно большой для представления размера любой строки, которую может обработать perl.
В маловероятном случае, когда для инициализации SV требуется более сложный подход, вы можете создать пустой SV с помощью newSV(len). Если len равно 0, возвращается пустой SV типа NULL, иначе возвращается SV типа PV с len + 1 (для NUL) байтами выделенной памяти, доступной через SvPVX. В обоих случаях SV имеет значение undef.
SV *sv = newSV(0); /* no storage allocated */
SV *sv = newSV(10); /* 10 (+1) bytes of uninitialised storage
* allocated */ Для изменения значения существующего SV существуют восемь процедур:
void sv_setiv(SV*, IV);
void sv_setuv(SV*, UV);
void sv_setnv(SV*, double);
void sv_setpv(SV*, const char*);
void sv_setpvn(SV*, const char*, STRLEN)
void sv_setpvf(SV*, const char*, ...);
void sv_vsetpvfn(SV*, const char*, STRLEN, va_list *,
SV **, Size_t, bool *);
void sv_setsv(SV*, SV*); Обратите внимание, что вы можете указать длину присваиваемой строки, используя sv_setpvn, newSVpvn или newSVpv, или позволить Perl рассчитать длину, используя sv_setpv или указав 0 в качестве второго аргумента для newSVpv. Однако будьте осторожны, Perl определит длину строки, используя strlen, которое зависит от того, что строка завершается символом NUL, а не содержит символы NULL иначе.
Аргументы sv_setpvf обрабатываются так же, как и sprintf, а отформатированный вывод становится значением.
sv_vsetpvfn является аналогом vsprintf, но позволяет указать либо указатель на список аргументов переменной длины, либо адрес и длину массива SV. Последний аргумент указывает на булево значение; при возврате, если это значение истинно, то для форматирования строки использовалась информация, специфичная для локали, а содержимое строки, следовательно, недостоверно (см. perlsec). Этот указатель может быть NULL, если эта информация не важна. Обратите внимание, что для этой функции вам необходимо указать длину формата.
Функции sv_set*() недостаточно универсальны для работы со значениями, имеющими «магию». См. "Магические виртуальные таблицы" в дальнейшем в этом документе.
Все SV, содержащие строки, должны завершаться символом NUL. Если строка не завершается символом NUL, существует риск возникновения ошибок core dump и повреждения кода, который передает строку в функции C или системные вызовы, ожидающие строку, завершенную символом NUL. Собственные функции Perl обычно добавляют конечный NUL по этой причине. Тем не менее, будьте очень осторожны при передаче строки, хранящейся в SV, в функцию C или системный вызов.
Для доступа к фактическому значению, на которое указывает SV, API Perl предоставляет несколько макросов, которые преобразуют фактический скалярный тип в IV, UV, двойное значение или строку:
-
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должен быть доступен в хранилище потоко-локальных данных в многопотоковом Perl. В любом случае, помните, что Perl допускает произвольные строки данных, которые могут содержать символы NULL и могут не завершаться символом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 для соответствия завершающему NULL:
Newx(buf, somesize+1, char);
/* ... fill in buf ... */
buf[somesize] = '\0';
sv_usepvn_flags(sv, buf, somesize, SV_SMAGIC | SV_HAS_TRAILING_NUL);
/* buf now belongs to perl, don't release it */ Если у вас есть SV и вы хотите узнать, какой тип данных Perl считает, что он хранит, вы можете использовать следующие макросы для проверки типа SV.
SvIOK(SV*)
SvNOK(SV*)
SvPOK(SV*) Вы можете получить и установить текущую длину строки, хранящейся в SV, с помощью следующих макросов:
SvCUR(SV*)
SvCUR_set(SV*, I32 val) Также вы можете получить указатель на конец строки, хранящейся в SV, с помощью макроса:
SvEND(SV*) Но обратите внимание, что эти последние три макроса действительны только если SvPOK() истинно.
Если вы хотите добавить что-то в конец строки, хранящейся в SV*, вы можете использовать следующие функции:
void sv_catpv(SV*, const char*);
void sv_catpvn(SV*, const char*, STRLEN);
void sv_catpvf(SV*, const char*, ...);
void sv_vcatpvfn(SV*, const char*, STRLEN, va_list *, SV **,
I32, bool);
void sv_catsv(SV*, SV*); Первая функция вычисляет длину добавляемой строки, используя strlen. Во второй вы сами указываете длину строки. Третья функция обрабатывает свои аргументы как sprintf и добавляет отформатированный вывод. Четвёртая функция работает как vsprintf. Вы можете указать адрес и длину массива 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 хранит фактические данные в связанном списке структур с типом данных 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 в HV:
hv_store( hv, "key", 3, &PL_sv_undef, 0 ); Это действительно сделает значение undef, но если вы попытаетесь изменить значение key, получите следующую ошибку:
Modification of non-creatable hash value attempted В Perl 5.8.0 &PL_sv_undef также использовался для маркировки заполнительных элементов в ограниченных массивах. Это приводило к тому, что такие элементы массива не отображались при итерации по массиву или при проверке ключей с помощью функции hv_exists.
Вы можете столкнуться с аналогичными проблемами при хранении &PL_sv_yes или &PL_sv_no в AV или HV. Попытка изменить такие элементы приведет к следующей ошибке:
Modification of a read-only value attempted Короче говоря, вы можете использовать специальные переменные &PL_sv_undef, &PL_sv_yes и &PL_sv_no с AV и HV, но вам нужно убедиться, что вы знаете, что делаете.
В целом, если вы хотите сохранить неопределенное значение в AV или HV, не следует использовать &PL_sv_undef, а вместо этого создать новое неопределенное значение с помощью функции newSV, например:
av_store( av, 42, newSV(0) );
hv_store( hv, "foo", 3, newSV(0), 0 ); Ссылки
Ссылки — это особый тип скаляров, которые указывают на другие типы данных (включая другие ссылки).
Чтобы создать ссылку, используйте одну из следующих функций:
SV* newRV_inc((SV*) thing);
SV* newRV_noinc((SV*) thing); Аргумент thing может быть любым из SV*, AV* или HV*. Функции идентичны за исключением того, что newRV_inc увеличивает счетчик ссылок thing, в то время как newRV_noinc нет. По историческим причинам newRV является синонимом для newRV_inc.
После получения ссылки вы можете использовать следующий макрос для разыменования ссылки:
SvRV(SV*) затем вызовите соответствующие процедуры, преобразуя возвращаемый SV* в AV* или HV*, если необходимо.
Чтобы определить, является ли SV ссылкой, можно использовать следующий макрос:
SvROK(SV*) Чтобы узнать, к какому типу значений ссылается ссылка, используйте следующий макрос и проверьте возвращаемое значение.
SvTYPE(SvRV(SV*)) Наиболее полезные возвращаемые типы:
SVt_PVAV Array
SVt_PVHV Hash
SVt_PVCV Code
SVt_PVGV Glob (possibly a file handle) Любое возвращаемое числовое значение, меньшее SVt_PVAV, будет скаляром какой-либо формы.
См. "svtype" в perlapi для получения дополнительной информации.
Ссылки и объекты классов
Ссылки также используются для поддержки объектно-ориентированного программирования. В лексиконе Perl OO объект — это просто ссылка, которая была благословлена в пакет (или класс). После благословления программист может использовать ссылку для доступа к различным методам в классе.
Ссылка может быть благословлена в пакет с помощью следующей функции:
SV* sv_bless(SV* sv, HV* stash); Аргумент sv должен быть значением ссылки. Аргумент stash определяет, к какому классу будет принадлежать ссылка. См. "Стек и глобальные переменные" для информации о преобразовании имен классов в стеки.
/* Работа в процессе */
Следующая функция повышает rv до ссылки, если она таковой не является. Создает новую SV для rv. Если classname не равно NULL, SV благословляется в указанный класс. Возвращается SV.
SV* newSVrv(SV* rv, const char* classname); Следующие три функции копируют целое число, целое беззнаковое число или двойное значение в SV, ссылка которого равна rv. SV благословляется, если classname не равно NULL.
SV* sv_setref_iv(SV* rv, const char* classname, IV iv);
SV* sv_setref_uv(SV* rv, const char* classname, UV uv);
SV* sv_setref_nv(SV* rv, const char* classname, NV iv); Следующая функция копирует значение указателя (адрес, а не строку!) в SV, ссылка которого равна rv. SV благословляется, если classname не равно NULL.
SV* sv_setref_pv(SV* rv, const char* classname, void* pv); Следующая функция копирует строку в SV, ссылка которого равна rv. Установите длину в 0, чтобы Perl вычислил длину строки. SV благословляется, если classname не равно NULL.
SV* sv_setref_pvn(SV* rv, const char* classname, char* pv,
STRLEN length); Следующая функция проверяет, благословлен ли SV в указанный класс. Она не проверяет отношения наследования.
int sv_isa(SV* sv, const char* name); Следующая функция проверяет, является ли SV ссылкой на благословленный объект.
int sv_isobject(SV* sv); Следующая функция проверяет, происходит ли SV от указанного класса. SV может быть либо ссылкой на благословленный объект, либо строкой, содержащей имя класса. Эта функция реализует функциональность UNIVERSAL::isa.
bool sv_derived_from(SV* sv, const char* name); Чтобы проверить, получили ли вы объект, производный от определенного класса, вам нужно написать:
if (sv_isobject(sv) && sv_derived_from(sv, class)) { ... } Создание новых переменных
Чтобы создать новую переменную Perl с неопределенным значением, доступной из скрипта Perl, используйте следующие функции в зависимости от типа переменной.
SV* get_sv("package::varname", GV_ADD);
AV* get_av("package::varname", GV_ADD);
HV* get_hv("package::varname", GV_ADD); Обратите внимание на использование GV_ADD в качестве второго параметра. Теперь новую переменную можно установить, используя соответствующие функции для типа данных.
Существуют дополнительные макросы, значения которых можно побитово объединить с аргументом GV_ADD, чтобы включить определенные дополнительные функции. Эти биты:
- GV_ADDMULTI
-
Помечает переменную как многократно определенную, предотвращая:
Name <varname> used only once: possible typoпредупреждение.
- GV_ADDWARN
-
Выводит предупреждение:
Had to create <varname> unexpectedlyесли переменная не существовала до вызова функции.
Если вы не укажете имя пакета, переменная создается в текущем пакете.
Счетчики ссылок и смертность
Perl использует механизм сбора мусора на основе счетчика ссылок. SV, AV или HV (xV для краткости в дальнейшем) начинают свою жизнь со счетчиком ссылок 1. Если счетчик ссылок xV когда-либо опустится до 0, он будет уничтожен, и его память станет доступной для повторного использования. На самом базовом внутреннем уровне счетчики ссылок можно изменять с помощью следующих макросов:
int SvREFCNT(SV* sv);
SV* SvREFCNT_inc(SV* sv);
void SvREFCNT_dec(SV* sv); (Также существуют версии макросов инкремента и декремента с суффиксами для случаев, когда полная общность этих базовых макросов может быть заменена на некоторую производительность.)
Однако то, как программист должен мыслить о ссылках, не так сильно связано с самим счетчиком ссылок, но с владением ссылками. Ссылка на xV может принадлежать любому из различных сущностей: другой xV, интерпретатор Perl, структура данных XS, фрагмент исполняемого кода или динамический контекст. xV обычно не знает, какие сущности владеют ссылками на него; он знает только, сколько ссылок существует, что и есть счетчик ссылок.
Для правильного управления счетчиками ссылок важно отслеживать, какие ссылки обрабатывает код XS. Программист всегда должен знать, откуда пришла ссылка и кто её владелец, и быть осведомленным о создании или уничтожении ссылок, а также об передаче владения. Поскольку владение не представлено явно в структурах данных xV, только счетчик ссылок должен реально поддерживаться кодом, и это означает, что понимание владения не очевидно в самом коде. Например, передача владения ссылкой от одного владельца другому не меняет счетчик ссылок вообще, поэтому может быть достигнута без какого-либо кода. (Код переноса не трогает ссылку, но должен гарантировать, что бывший владелец знает, что он больше не владеет ссылкой, и что новый владелец теперь ею владеет.)
xV, видимый на уровне Perl, не должен становиться не ссылаемым и, следовательно, уничтожаться. Обычно объект становится не ссылаемым только тогда, когда он больше не видим, часто тем же способом, который делает его невидимым. Например, значение Perl-ссылки (RV) владеет ссылкой на свой referent, поэтому если RV перезаписан, эта ссылка уничтожается, а referent, на который больше нет ссылок, может быть уничтожен в результате.
Многие функции включают некоторую манипуляцию ссылками как часть их предназначения. Иногда это документируется с точки зрения владения ссылками, а иногда (менее информативно) с точки зрения изменений в счетчиках ссылок. Например, функция newRV_inc() документирована как создание нового RV (со счетчиком ссылок 1) и увеличение счетчика ссылок referent, предоставленного вызывающей стороной. Это лучше всего понять как создание новой ссылки на referent, которым владеет созданный RV, и возвращение вызывающей стороне владения единственной ссылкой на RV. Функция newRV_noinc() вместо этого не увеличивает счетчик ссылок referent, но RV тем не менее получает ссылку на referent. Следовательно, подразумевается, что вызывающая сторона newRV_noinc() отказывается от ссылки на referent, что делает эту операцию концептуально более сложной, даже если она выполняет меньше действий над структурами данных.
Например, представьте, что вы хотите вернуть ссылку из функции XSUB. Внутри процедуры XSUB вы создаете SV, который изначально имеет только одну ссылку, которой владеет процедура XSUB. Эта ссылка должна быть удалена до завершения процедуры, иначе она станет утечкой, предотвращая уничтожение SV. Таким образом, для создания RV, ссылающегося на SV, удобнее всего передать SV функции newRV_noinc(), которая использует эту ссылку. Теперь процедура XSUB больше не владеет ссылкой на SV, но владеет ссылкой на RV, который, в свою очередь, владеет ссылкой на SV. Затем владение ссылкой на RV передается процессом возвращения RV из XSUB.
Доступны некоторые вспомогательные функции, которые могут помочь в уничтожении xVs. Эти функции вводят понятие «смертность». В значительной документации говорится о том, что сам xV смертен, но это вводит в заблуждение. Речь идет о ссылке на xV, которая смертна, и возможно, что существует более одной смертной ссылки на один xV. Ссылка смертна, если она принадлежит стеку временных переменных, одному из многих внутренних стеков Perl, который уничтожит эту ссылку «немного позже». Обычно «немного позже» — это конец текущей инструкции Perl. Однако это усложняется в динамических областях: могут существовать несколько наборов смертных ссылок, существующих одновременно, с различными датами смерти. Внутренне, фактическим определяющим моментом для уничтожения смертных ссылок на xV являются два макроса, SAVETMPS и FREETMPS. См. perlcall, perlxs и "Стек временных переменных" ниже для получения более подробной информации об этих макросах.
Смертные ссылки в основном используются для xVs, которые помещаются в основной стек Perl. Стек проблематичен для отслеживания ссылок, потому что он содержит множество ссылок на xV, но не владеет этими ссылками: они не учитываются. В настоящее время существует множество ошибок, возникающих из-за уничтожения xVs, когда на них ссылается стек, так как неучтенные ссылки стека недостаточны для сохранения xVs в живых. Поэтому при размещении (неучтенной) ссылки в стеке крайне важно убедиться, что будет существовать учтённая ссылка на тот же 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 (Glob Value). Этот 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); Большинство из них связаны с форматами.
IoFLAGs() может содержать комбинацию флагов, наиболее интересными из которых являются IOf_FLUSH ($|) для автоматической записи и IOf_UNTAINT, устанавливаемого с помощью метода IO::Handle's untaint().
Объект IO также может содержать дескриптор каталога:
DIR *IoDIRP(io); пригодный для использования с PerlDir_read() и т. п.
Все эти макросы-аксессоры являются lvalues, нет отдельных _set() макросов для изменения элементов объекта IO.
SV с двойным типом
Скалярные переменные обычно содержат только один тип значения: целое число, двойная точность, указатель или ссылка. Perl автоматически преобразует фактические скалярные данные из хранящегося типа в запрашиваемый.
Некоторые скалярные переменные содержат более одного типа скалярных данных. Например, переменная $! содержит либо числовое значение errno, либо его строковое эквивалент из strerror или sys_errlist[].
Чтобы принудительно поместить несколько значений данных в SV, необходимо сделать два шага: использовать процедуры sv_set*v для добавления дополнительного скалярного типа, затем установить флаг, чтобы Perl понимал, что он содержит более одного типа данных. Четыре макроса для установки флагов:
SvIOK_on
SvNOK_on
SvPOK_on
SvROK_on Конкретный макрос, который вы должны использовать, зависит от того, какую процедуру sv_set*v вы вызвали первой. Это потому, что каждая процедура sv_set*v включает только бит для конкретного типа устанавливаемых данных и выключает все остальные.
Например, чтобы создать новую переменную Perl с именем «dberror», которая содержит как числовые, так и описательные строковые значения ошибок, можно использовать следующий код:
extern int dberror;
extern char *dberror_list;
SV* sv = get_sv("dberror", GV_ADD);
sv_setiv(sv, (IV) dberror);
sv_setpv(sv, dberror_list[dberror]);
SvIOK_on(sv); Если порядок sv_setiv и sv_setpv был обратным, то необходимо было бы вызвать макрос SvPOK_on вместо SvIOK_on.
Значения только для чтения
В Perl 5.16 и более ранних версиях механизм копирования при записи (см. следующий раздел) использовал один и тот же бит флага со скалярами только для чтения. Поэтому единственный способ проверить, вызовет ли sv_setsv и т. д. ошибку «Изменение значения только для чтения» в этих версиях, это:
SvREADONLY(sv) && !SvIsCOW(sv) В Perl 5.18 и более поздних версиях SvREADONLY применяется только к переменным только для чтения, а в версии 5.20 скаляры с копированием при записи также могут быть только для чтения, поэтому вышеприведённая проверка неверна. Вам нужно только:
SvREADONLY(sv) Если вам часто нужно выполнять эту проверку, определите свой собственный макрос следующим образом:
#if PERL_VERSION >= 18
# define SvTRULYREADONLY(sv) SvREADONLY(sv)
#else
# define SvTRULYREADONLY(sv) (SvREADONLY(sv) && !SvIsCOW(sv))
#endif Копирование при записи
Perl реализует механизм копирования при записи (COW) для скаляров, при котором копии строк не создаются сразу при запросе, а откладываются до тех пор, пока одно или оба скалярные значения не изменятся. Это в основном прозрачно, но необходимо следить за тем, чтобы не изменять буферы строк, которые совместно используются несколькими SV.
Вы можете проверить, использует ли SV копирование при записи, с помощью SvIsCOW(sv).
Вы можете заставить SV создать свою копию буфера строки, вызвав sv_force_normal(sv) или SvPV_force_nolen(sv).
Если вы хотите заставить SV сбросить свой буфер строки, используйте sv_force_normal_flags(sv, SV_COW_DROP_PV) или просто sv_setsv(sv, NULL).
Все эти функции сгенерируют ошибку для скаляров только для чтения (см. предыдущий раздел для получения дополнительной информации об этом).
Чтобы проверить, что ваш код ведет себя правильно и не изменяет буферы COW, в системах, поддерживающих mmap(2) (то есть Unix), вы можете сконфигурировать Perl с помощью -Accflags=-DPERL_DEBUG_READONLY_COW, и он преобразует нарушения буферов в сбои. Вы обнаружите, что это очень медленно, поэтому вы можете пропустить собственные тесты Perl.
Магические переменные
[Этот раздел ещё в разработке. Проигнорируйте всё здесь. Не размещайте объявления. Всё, что не разрешено, запрещено.]
Любой SV может быть магическим, то есть он имеет специальные функции, которых нет у обычного SV. Эти функции хранятся в структуре SV в связанном списке struct magic, переименованном в MAGIC.
struct magic {
MAGIC* mg_moremagic;
MGVTBL* mg_virtual;
U16 mg_private;
char mg_type;
U8 mg_flags;
I32 mg_len;
SV* mg_obj;
char* mg_ptr;
}; Обратите внимание, что это актуальная информация на уровне патча 0 и может измениться в любое время.
Назначение магии
Perl добавляет магию к SV с помощью функции sv_magic:
void sv_magic(SV* sv, SV* obj, int how, const char* name, I32 namlen); Аргумент sv — это указатель на SV, которому нужно добавить новую магическую функцию.
Если sv ещё не магический, Perl использует макрос SvUPGRADE, чтобы преобразовать sv в тип SVt_PVMG. Затем Perl добавляет новую магию в начало связанного списка магических функций. Любая предыдущая запись того же типа магии удаляется. Обратите внимание, что это можно переопределить, и к одному SV можно назначить несколько экземпляров одного типа магии.
Аргументы name и namlen используются для связывания строки с магией, обычно с именем переменной. namlen хранится в поле mg_len, и если name не равно null, то либо копия savepvn name, либо само name хранится в поле mg_ptr, в зависимости от того, больше ли namlen нуля или равно нулю соответственно. В качестве специального случая, если (name && namlen == HEf_SVKEY), то предполагается, что name содержит SV* и хранится как есть с увеличенным значением REFCNT.
Функция sv_magic использует how для определения того, какой, если таковой имеется, предопределённой «Магической виртуальной таблицы» следует назначить полю mg_virtual. См. раздел "Магические виртуальные таблицы" ниже. Аргумент how также сохраняется в поле mg_type. Значение how должно выбираться из набора макросов PERL_MAGIC_foo, находящихся в файле perl.h. Обратите внимание, что до добавления этих макросов внутренние компоненты Perl напрямую использовали символьные литералы, поэтому вы иногда можете встретить старый код или документацию, ссылающуюся на магию 'U' вместо PERL_MAGIC_uvar, например.
Аргумент obj хранится в поле mg_obj структуры MAGIC. Если оно не совпадает с аргументом sv, счётчик ссылок объекта obj увеличивается. Если они совпадают, или если аргумент how равен PERL_MAGIC_arylen, PERL_MAGIC_regdatum, PERL_MAGIC_regdata или является указателем null, то obj просто сохраняется без увеличения счётчика ссылок.
См. также sv_magicext в perlapi для более гибкого способа добавления магии к SV.
Также существует функция для добавления магии к HV:
void hv_magic(HV *hv, GV *gv, int how); Она просто вызывает sv_magic и преобразует аргумент gv в SV.
Чтобы удалить магию из SV, вызовите функцию sv_unmagic:
int sv_unmagic(SV *sv, int type); Аргумент type должен быть равен значению how, когда SV изначально стал магическим.
Однако обратите внимание, что sv_unmagic удаляет всю магию определённого type из SV. Если вы хотите удалить только определённую магию type, основанную на магической виртуальной таблице, используйте sv_unmagicext вместо этого:
int sv_unmagicext(SV *sv, int type, MGVTBL *vtbl); Магические виртуальные таблицы
Поле mg_virtual в структуре MAGIC является указателем на MGVTBL, которая представляет собой структуру указателей на функции и называется «Магической виртуальной таблицей» для обработки различных операций, которые могут быть применены к этой переменной.
MGVTBL имеет пять (или иногда восемь) указателей на следующие типы процедур:
int (*svt_get) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_set) (pTHX_ SV* sv, MAGIC* mg);
U32 (*svt_len) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_clear)(pTHX_ SV* sv, MAGIC* mg);
int (*svt_free) (pTHX_ SV* sv, MAGIC* mg);
int (*svt_copy) (pTHX_ SV *sv, MAGIC* mg, SV *nsv,
const char *name, I32 namlen);
int (*svt_dup) (pTHX_ MAGIC *mg, CLONE_PARAMS *param);
int (*svt_local)(pTHX_ SV *nsv, MAGIC *mg); Структура MGVTBL устанавливается во время компиляции в perl.h, и в настоящее время существует 32 типа. Эти различные структуры содержат указатели на различные процедуры, выполняющие дополнительные действия в зависимости от вызываемой функции.
Function pointer Action taken
---------------- ------------
svt_get Do something before the value of the SV is
retrieved.
svt_set Do something after the SV is assigned a value.
svt_len Report on the SV's length.
svt_clear Clear something the SV represents.
svt_free Free any extra storage associated with the SV.
svt_copy copy tied variable magic to a tied element
svt_dup duplicate a magic structure during thread cloning
svt_local copy magic to local value during 'local' Например, структура MGVTBL, называемая vtbl_sv (которая соответствует типу mg_type PERL_MAGIC_sv), содержит:
{ magic_get, magic_set, magic_len, 0, 0 } Таким образом, когда SV определяется как магический и типа PERL_MAGIC_sv, если выполняется операция получения, вызывается процедура magic_get. Все различные процедуры для различных магических типов начинаются с magic_. ПРИМЕЧАНИЕ: магические процедуры не считаются частью Perl API и могут не экспортироваться библиотекой Perl.
Последние три слота — это недавнее добавление, и для совместимости с исходным кодом они проверяются только в том случае, если один из трёх флагов MGf_COPY, MGf_DUP или MGf_LOCAL установлен в mg_flags. Это означает, что большинство кода может продолжать объявлять vtable как 5-элементное значение. Эти три в настоящее время используются исключительно кодом потоков и могут быть изменены.
Текущие типы магических виртуальных таблиц:
mg_type
(old-style char and macro) MGVTBL Type of magic
-------------------------- ------ -------------
\0 PERL_MAGIC_sv vtbl_sv Special scalar variable
# PERL_MAGIC_arylen vtbl_arylen Array length ($#ary)
% PERL_MAGIC_rhash (none) Extra data for restricted
hashes
* PERL_MAGIC_debugvar vtbl_debugvar $DB::single, signal, trace
vars
. PERL_MAGIC_pos vtbl_pos pos() lvalue
: PERL_MAGIC_symtab (none) Extra data for symbol
tables
< PERL_MAGIC_backref vtbl_backref For weak ref data
@ PERL_MAGIC_arylen_p (none) To move arylen out of XPVAV
B PERL_MAGIC_bm vtbl_regexp Boyer-Moore
(fast string search)
c PERL_MAGIC_overload_table vtbl_ovrld Holds overload table
(AMT) on stash
D PERL_MAGIC_regdata vtbl_regdata Regex match position data
(@+ and @- vars)
d PERL_MAGIC_regdatum vtbl_regdatum Regex match position data
element
E PERL_MAGIC_env vtbl_env %ENV hash
e PERL_MAGIC_envelem vtbl_envelem %ENV hash element
f PERL_MAGIC_fm vtbl_regexp Formline
('compiled' format)
g PERL_MAGIC_regex_global vtbl_mglob m//g target
H PERL_MAGIC_hints vtbl_hints %^H hash
h PERL_MAGIC_hintselem vtbl_hintselem %^H hash element
I PERL_MAGIC_isa vtbl_isa @ISA array
i PERL_MAGIC_isaelem vtbl_isaelem @ISA array element
k PERL_MAGIC_nkeys vtbl_nkeys scalar(keys()) lvalue
L PERL_MAGIC_dbfile (none) Debugger %_<filename
l PERL_MAGIC_dbline vtbl_dbline Debugger %_<filename
element
N PERL_MAGIC_shared (none) Shared between threads
n PERL_MAGIC_shared_scalar (none) Shared between threads
o PERL_MAGIC_collxfrm vtbl_collxfrm Locale transformation
P PERL_MAGIC_tied vtbl_pack Tied array or hash
p PERL_MAGIC_tiedelem vtbl_packelem Tied array or hash element
q PERL_MAGIC_tiedscalar vtbl_packelem Tied scalar or handle
r PERL_MAGIC_qr vtbl_regexp Precompiled qr// regex
S PERL_MAGIC_sig (none) %SIG hash
s PERL_MAGIC_sigelem vtbl_sigelem %SIG hash element
t PERL_MAGIC_taint vtbl_taint Taintedness
U PERL_MAGIC_uvar vtbl_uvar Available for use by
extensions
u PERL_MAGIC_uvar_elem (none) Reserved for use by
extensions
V PERL_MAGIC_vstring (none) SV was vstring literal
v PERL_MAGIC_vec vtbl_vec vec() lvalue
w PERL_MAGIC_utf8 vtbl_utf8 Cached UTF-8 information
x PERL_MAGIC_substr vtbl_substr substr() lvalue
Y PERL_MAGIC_nonelem vtbl_nonelem Array element that does not
exist
y PERL_MAGIC_defelem vtbl_defelem Shadow "foreach" iterator
variable / smart parameter
vivification
\ PERL_MAGIC_lvref vtbl_lvref Lvalue reference
constructor
] PERL_MAGIC_checkcall vtbl_checkcall Inlining/mutation of call
to this CV
~ PERL_MAGIC_ext (none) Available for use by
extensions Когда в таблице присутствуют как прописная, так и строчная буква, заглавная буква обычно используется для представления некоторого составного типа (список или хеш), а строчная буква используется для представления элемента этого составного типа. Некоторые внутренние коды используют это соответствие регистров. Однако 'v' и 'V' (vec и v-строка) никак не связаны.
Магические типы PERL_MAGIC_ext и PERL_MAGIC_uvar определены специально для использования расширениями и не будут использоваться самим Perl. Расширения могут использовать магию PERL_MAGIC_ext, чтобы «присоединить» частную информацию к переменным (обычно к объектам). Это особенно полезно, потому что обычный perl-код не может испортить эту частную информацию (в отличие от использования дополнительных элементов объекта хеша).
Аналогично, магия PERL_MAGIC_uvar может использоваться так же, как tie(), для вызова C-функции всякий раз, когда используется или изменяется значение скаляра. Поле MAGIC's mg_ptr указывает на структуру ufuncs:
struct ufuncs {
I32 (*uf_val)(pTHX_ IV, SV*);
I32 (*uf_set)(pTHX_ IV, SV*);
IV uf_index;
}; При чтении или записи SV вызываются функции uf_val или uf_set с uf_index в качестве первого аргумента и указателем на SV во втором. Простой пример добавления магии PERL_MAGIC_uvar показан ниже. Обратите внимание, что структура ufuncs копируется функцией sv_magic, поэтому вы можете безопасно выделить её в стеке.
void
Umagic(sv)
SV *sv;
PREINIT:
struct ufuncs uf;
CODE:
uf.uf_val = &my_get_fn;
uf.uf_set = &my_set_fn;
uf.uf_index = 0;
sv_magic(sv, 0, PERL_MAGIC_uvar, (char*)&uf, sizeof(uf)); Прикрепление PERL_MAGIC_uvar к массивам разрешено, но не имеет никакого эффекта.
Для хешей есть специализированный обработчик, который даёт контроль над ключами хешей (но не значениями). Этот обработчик вызывает магию 'get' PERL_MAGIC_uvar, если функция "set" в структуре ufuncs равна NULL. Обработчик активируется всякий раз, когда к хешу обращаются с ключом, указанным как SV через функции hv_store_ent, hv_fetch_ent, hv_delete_ent и hv_exists_ent. Доступ к ключу как строке через функции без суффикса ..._ent обходит обработчик. См. "GUTS" в Hash::Util::FieldHash для подробного описания.
Обратите внимание, что поскольку несколько расширений могут использовать магию PERL_MAGIC_ext или PERL_MAGIC_uvar, важно, чтобы расширения проявляли особую осторожность, чтобы избежать конфликтов. Обычно достаточно использовать магию только для объектов, благословлённых тем же классом, что и расширение. Для магии PERL_MAGIC_ext обычно рекомендуется определить структуру MGVTBL, даже если все её поля будут 0, чтобы отдельные указатели MAGIC можно было идентифицировать как определённый вид магии с помощью их магической виртуальной таблицы. mg_findext предоставляет лёгкий способ сделать это:
STATIC MGVTBL my_vtbl = { 0, 0, 0, 0, 0, 0, 0, 0 };
MAGIC *mg;
if ((mg = mg_findext(sv, PERL_MAGIC_ext, &my_vtbl))) {
/* this is really ours, not another module's PERL_MAGIC_ext */
my_priv_data_t *priv = (my_priv_data_t *)mg->mg_ptr;
...
} Также обратите внимание, что функции sv_set*() и sv_cat*(), описанные ранее, не вызывают магию 'set' для своих целей. Это должно выполняться пользователем, либо вызывая макрос SvSETMAGIC() после вызова этих функций, либо используя одну из функций sv_set*_mg() или sv_cat*_mg(). Аналогично, общий C-код должен вызвать макрос SvGETMAGIC() для вызова любой магии 'get', если они используют SV, полученный из внешних источников, в функциях, которые не обрабатывают магию. См. perlapi для описания этих функций. Например, вызовы функций sv_cat*() обычно требуют последующего вызова SvSETMAGIC(), но им не нужен предварительный SvGETMAGIC(), так как их реализация обрабатывает магию 'get'.
Поиск магии
MAGIC *mg_find(SV *sv, int type); /* Finds the magic pointer of that
* type */ Эта процедура возвращает указатель на структуру MAGIC, хранящуюся в SV. Если SV не имеет этого магического свойства, возвращается NULL. Если SV имеет несколько экземпляров этого магического свойства, будет возвращён первый. mg_findext может быть использован для поиска структуры MAGIC SV, основываясь на его магическом типе и магической виртуальной таблице:
MAGIC *mg_findext(SV *sv, int type, MGVTBL *vtbl); Также, если SV, переданный в mg_find или mg_findext, не является типом SVt_PVMG, Perl может завершиться с ошибкой.
int mg_copy(SV* sv, SV* nsv, const char* key, STRLEN klen); Эта процедура проверяет, какие типы магии sv имеет. Если поле mg_type — заглавная буква, то mg_obj копируется в nsv, но поле mg_type изменяется на строчную букву.
Понимание магии привязанных хешей и массивов
Привязанные хеши и массивы — магические существа типа магии PERL_MAGIC_tied.
ПРЕДУПРЕЖДЕНИЕ: Начиная с версии 5.004, для правильного использования функций доступа к массивам и хешам необходимо понимать некоторые нюансы. Некоторые из этих нюансов на самом деле считаются ошибками в API, которые должны быть исправлены в последующих выпусках, и помещены в квадратные скобки [MAYCHANGE] ниже. Если вы обнаружите, что на самом деле применяете такую информацию в этом разделе, имейте в виду, что поведение может измениться в будущем, эмм, без предупреждения.
Функция perl tie связывает переменную с объектом, который реализует различные методы GET, SET и т. д. Для имитации функции perl tie из XSUB необходимо воспроизвести это поведение. Приведённый ниже код выполняет необходимые шаги — сначала он создаёт новый хеш, а затем создаёт второй хеш, который благословляет в класс, который будет реализовывать методы tie. В конечном итоге он связывает два хеша и возвращает ссылку на новый привязанный хеш. Обратите внимание, что приведённый ниже код НЕ вызывает метод TIEHASH в классе MyTie — см. "Вызов perl-процедур из C-программ" для получения подробностей о том, как это сделать.
SV*
mytie()
PREINIT:
HV *hash;
HV *stash;
SV *tie;
CODE:
hash = newHV();
tie = newRV_noinc((SV*)newHV());
stash = gv_stashpv("MyTie", GV_ADD);
sv_bless(tie, stash);
hv_magic(hash, (GV*)tie, PERL_MAGIC_tied);
RETVAL = newRV_noinc(hash);
OUTPUT:
RETVAL Функция av_store, когда ей передаётся привязанный массив в качестве аргумента, просто копирует магию массива на значение, которое нужно «сохранить», используя mg_copy. Она также может вернуть NULL, что указывает на то, что значение фактически не нужно хранить в массиве. [MAYCHANGE] После вызова av_store на привязанном массиве вызывающий обычно должен вызвать mg_set(val), чтобы фактически вызвать perl-уровень «STORE» метод на объекте TIEARRAY. Если av_store вернула NULL, обычно также будет необходим вызов SvREFCNT_dec(val), чтобы избежать утечки памяти. [/MAYCHANGE]
Предыдущий абзац полностью применим к доступу к привязанным хешам с использованием функций hv_store и hv_store_ent.
av_fetch и соответствующие функции хеширования hv_fetch и hv_fetch_ent фактически возвращают неопределенное значение смертного типа, чья магия была инициализирована с использованием mg_copy. Обратите внимание, что возвращаемое значение не нужно освобождать, так как оно уже смертного типа. [MAYCHANGE] Но вам нужно будет вызвать mg_get() для возвращенного значения, чтобы фактически вызвать метод "FETCH" на уровне Perl для базового объекта TIE. Аналогично, вы также можете вызвать mg_set() для возвращаемого значения после, возможно, присвоения подходящего значения с использованием sv_setsv, что вызовет метод "STORE" для объекта TIE.
[MAYCHANGE] Другими словами, функции извлечения/записи массивов или хешей на самом деле не извлекают и не записывают фактические значения в случае связанных массивов и хешей. Они просто вызывают mg_copy, чтобы прикрепить магию к значениям, которые должны были быть "записаны" или "извлечены". Позже вызовы mg_get и mg_set фактически выполняют работу по вызову методов TIE для базовых объектов. Таким образом, механизм магии в настоящее время реализует своего рода леничный доступ к массивам и хешам.
В настоящее время (начиная с версии Perl 5.004) использование функций доступа к хешам и массивам требует, чтобы пользователь был осведомлен о том, работает ли он с "обычными" хешами и массивами или с их связанными вариантами. В будущих версиях API может быть изменено, чтобы обеспечить более прозрачный доступ к связанным и обычным типам данных. [/MAYCHANGE]
Вам следует понять, что интерфейсы TIEARRAY и TIEHASH — это просто удобный синтаксис для вызова некоторых методов Perl, используя унифицированный синтаксис массивов и хешей. Использование этого синтаксиса накладывает некоторую нагрузку (как правило, около двух-четырёх дополнительных опкодов на операцию FETCH/STORE, помимо создания всех переменных смертного типа, необходимых для вызова методов). Эта нагрузка будет сравнительно небольшой, если методы TIE сами по себе значительные, но если они содержат только несколько операторов, то эта нагрузка будет существенной.
Локализация изменений
Perl имеет очень удобную конструкцию
{
local $var = 2;
...
} Эта конструкция приблизительно эквивалентна
{
my $oldvar = $var;
$var = 2;
...
$var = $oldvar;
} Основное отличие состоит в том, что первая конструкция восстановит начальное значение $var независимо от того, каким образом управление выходит из блока: goto, return, die/eval и т. д. Она также немного более эффективна.
Существует способ достижения аналогичной задачи из C через API Perl: создать псевдоблок и организовать автоматическое отмену некоторых изменений в конце блока, явным образом или через выход за пределы локальной области видимости (через die()). Блок-подобная конструкция создаётся с помощью пары макросов ENTER/LEAVE (см. "Возвращение скаляра" в perlcall). Такая конструкция может быть создана специально для какой-либо важной задачи локализации или может быть использована существующая (например, границы окружающего подпрограммы Perl/блока или существующая пара для освобождения временных переменных). (В последнем случае дополнительная нагрузка локализации должна быть практически незначительной.) Обратите внимание, что любой XSUB автоматически заключён в пару ENTER/LEAVE.
Внутри такого псевдоблока доступна следующая услуга:
-
SAVEINT(int i) -
SAVEIV(IV i) -
SAVEI32(I32 i) -
SAVELONG(long i) -
SAVEI8(I8 i) -
SAVEI16(I16 i) -
SAVEBOOL(int i) -
Эти макросы организуют восстановление значения целочисленной переменной
iв конце окружающего псевдоблока. -
SAVESPTR(s) -
SAVEPPTR(p) -
Эти макросы организуют восстановление значений указателей
sиp.sдолжен быть указателем типа, который выдерживает преобразование вSV*и обратно,pдолжен быть способен выдержать преобразование вchar*и обратно. -
SAVEFREESV(SV *sv) -
Счётчик ссылок
svбудет уменьшен в конце псевдоблока. Это аналогичноsv_2mortal, поскольку это также механизм для выполнения отложеннойSvREFCNT_dec. Однако, в то время какsv_2mortalпродлевает срок жизниsvдо начала следующего оператора,SAVEFREESVпродлевает его до конца окружающего блока. Эти сроки жизни могут значительно отличаться.Также сравните
SAVEMORTALIZESV. -
SAVEMORTALIZESV(SV *sv) -
Как и
SAVEFREESV, но смерщаетsvв конце текущей области видимости вместо уменьшения счётчика ссылок. Это обычно приводит к тому, чтоsvостаётся живым до выполнения оператора, который вызвал текущую область видимости. -
SAVEFREEOP(OP *op) -
OP *освобождается при помощи op_free() в конце псевдоблока. -
SAVEFREEPV(p) -
Блок памяти, на который указывает
p, освобождается с помощью Safefree() в конце псевдоблока. -
SAVECLEARSV(SV *sv) -
Очищает слот в текущем наборе временных переменных, который соответствует
sv, в конце псевдоблока. -
SAVEDELETE(HV *hv, char *key, I32 length) -
Ключ
keyhvудаляется в конце псевдоблока. Строка, на которую указываетkey, освобождается с помощью Safefree(). Если у вас есть ключ в хранилище с коротким сроком жизни, соответствующая строка может быть перераспределена следующим образом:SAVEDELETE(PL_defstash, savepv(tmpbuf), strlen(tmpbuf)); -
SAVEDESTRUCTOR(DESTRUCTORFUNC_NOCONTEXT_t f, void *p) -
В конце псевдоблока функция
fвызывается с единственным аргументомp. -
SAVEDESTRUCTOR_X(DESTRUCTORFUNC_t f, void *p) -
В конце псевдоблока функция
fвызывается с неявным контекстным аргументом (если он есть) иp. -
SAVESTACK_POS() -
Текущий смещение в внутренней стеке Perl (см.
SP) восстанавливается в конце псевдоблока.
В приведенном ниже списке API содержатся функции, поэтому необходимо явно указать указатели на изменяемые данные (либо C-указатели, либо перльские GV *). Там, где вышеприведенные макросы принимают int, аналогичная функция принимает int *.
Другие макросы выше имеют реализующие их функции, но, вероятно, лучше просто использовать макрос, а не их или те, что ниже.
-
SV* save_scalar(GV *gv) -
Эквивалентно коду Perl
local $gv. -
AV* save_ary(GV *gv) -
HV* save_hash(GV *gv) -
Аналогично
save_scalar, но локализует@gvи%gv. -
void save_item(SV *item) -
Дублирует текущее значение
SV. При выходе из текущего псевдоблокаENTER/LEAVEзначениеSVбудет восстановлено с помощью сохранённого значения. Магией она не управляет. Используйтеsave_scalar, если магией необходимо управлять. -
void save_list(SV **sarg, I32 maxsarg) -
Вариант
save_item, который принимает несколько аргументов через массивsargизSV*длинойmaxsarg. -
SV* save_svref(SV **sptr) -
Аналогично
save_scalar, но восстановитSV *. -
void save_aptr(AV **aptr) -
void save_hptr(HV **hptr) -
Аналогично
save_svref, но локализуетAV *иHV *.
Модуль Alias реализует локализацию основных типов внутри области видимости вызывающего. Те, кто интересуется тем, как локализовать вещи в содержащей области видимости, также должны взглянуть на него.
Подпрограммы
XSUB и стек аргументов
Механизм XSUB — это простой способ для программ Perl доступа к C-подпрограммам. Подпрограмма XSUB будет иметь стек, содержащий аргументы из программы Perl, и способ сопоставления перльских структур данных с C-эквивалентами.
Аргументы стека доступны через макрос ST(n), который возвращает n-й аргумент стека. Аргумент 0 — это первый аргумент, переданный в вызов подпрограммы Perl. Эти аргументы являются SV* и могут использоваться везде, где используется SV*.
Большинство раз output от 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-значения в перловый стек".
Для получения дополнительной информации, обратитесь к 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 (целевые), которые (как следствие) не постоянно освобождаются/создаются.
Каждый из целевых создаётся только один раз (но см. "Scratchpads и рекурсия" ниже), и когда оператор должен поместить целое число, двойное число или строку в стек, он просто устанавливает соответствующие части своего целевого и помещает целевого в стек.
Макрос для помещения этого целевого в стек — PUSHTARG, и он непосредственно используется в некоторых операторах, а также косвенно в бесчисленных других, которые используют его через (X)PUSH[iunp].
Поскольку целевой объект переиспользуется, нужно быть внимательным при помещении нескольких значений в стек. Следующий код не сделает того, что вы ожидаете:
XPUSHi(10);
XPUSHi(20); Это переводится как «установить TARG в 10, поместить указатель на TARG в стек; установить TARG в 20, поместить указатель на TARG в стек». В конце операции стек не содержит значения 10 и 20, а фактически содержит два указателя на TARG, которое мы установили в 20.
Если вам нужно поместить несколько различных значений, тогда вы можете использовать макросы (X)PUSHs или новые макросы m(X)PUSH[iunp], ни один из которых не использует TARG. Макросы (X)PUSHs просто помещают SV* в стек, что, как отмечается в разделе "XSUB и стек аргументов", часто потребует «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.
Scratchpads
Вопрос остаётся в том, когда создаются SV, которые являются целевыми для операторов. Ответ заключается в том, что они создаются, когда компилируется текущая единица — подпрограмма или файл (для операторов для операторов вне подпрограмм).
Scratchpad хранит SV, которые являются локальными переменными для текущей единицы и являются целевыми для операторов. Предыдущая версия этого документа утверждала, что можно определить, живёт ли SV в scratchpad, посмотрев на его флаги: локальные переменные имеют SVs_PADMY, а целевые имеют SVs_PADTMP. Но это никогда не было полностью верно. SVs_PADMY может быть установлено на переменной, которая больше не находится в любом блоке. Хотя целевые имеют SVs_PADTMP, оно также может быть установлено на переменных, которые никогда не находились в блоке, но тем не менее ведут себя как целевые. Начиная с perl 5.21.5, флаг SVs_PADMY больше не используется и определён как 0. SvPADMY() теперь возвращает true для всего, у чего нет SVs_PADTMP.
Соответствие между OP и целевыми не является 1 к 1. Разные OP в дереве компиляции единицы могут использовать один и тот же целевой объект, если это не будет противоречить ожидаемой жизни временной переменной.
Scratchpads и рекурсия
На самом деле не совсем верно, что скомпилированная единица содержит указатель на массив scratchpad. На самом деле она содержит указатель на массив (вначале) из одного элемента, и этот элемент — массив scratchpad. Зачем нам нужна дополнительный уровень косвенности?
Ответ — рекурсия, и, возможно, потоки. Оба этих случая могут создавать несколько указателей выполнения, переходящих в одну и ту же подпрограмму. Чтобы дочерняя подпрограмма не перезаписывала временные переменные для родительской подпрограммы (жизненный цикл которой охватывает вызов дочерней), родительская и дочерняя подпрограммы должны иметь разные scratchpads. (И локальные переменные должны быть отдельными!)
Итак, каждая подпрограмма рождается с массивом scratchpads (длиной 1). При каждом входе в подпрограмму проверяется, не превышает ли текущая глубина рекурсии длину этого массива, и если превышает, создаётся новый scratchpad и добавляется в массив.
Целевые в этом scratchpad — 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 экземпляров размера структуры данных sizeof (используя функцию sizeof).
PerlIO
Последние версии Perl экспериментируют с удалением зависимости Perl от стандартного набора стандартного ввода/вывода и позволяют использовать другие реализации stdio. Это подразумевает создание нового уровня абстракции, который затем вызывает реализацию stdio, с которой был скомпилирован Perl. Все XSUB теперь должны использовать функции уровня абстракции PerlIO, а не делать никаких предположений о том, какой stdio используется.
Для полного описания абстракции PerlIO, обратитесь к perlapio.
Скомпилированный код
Дерево кода
Здесь мы опишем внутреннюю форму, в которую ваш код преобразуется Perl. Начните с простого примера:
$a = $b + $c; Это преобразуется в дерево, подобное этому:
assign-to
/ \
+ $a
/ \
$b $c (но немного сложнее). Это дерево отражает способ, которым Perl проанализировал ваш код, но не имеет никакого отношения к порядку выполнения. Существует дополнительный «поток», проходящий через узлы дерева, который показывает порядок выполнения узлов. В нашем упрощённом примере выше он выглядит так:
$b ---> $c ---> + ---> $a ---> assign-to Но с фактическим деревом компиляции для $a = $b + $c это отличается: некоторые узлы оптимизированы. Как следствие, хотя фактическое дерево содержит больше узлов, чем наш упрощённый пример, порядок выполнения такой же, как в нашем примере.
Просмотр дерева
Если ваш Perl скомпилирован для отладки (обычно делается с -DDEBUGGING на Configure командной строке), вы можете просмотреть скомпилированное дерево, указав -Dx в командной строке Perl. Вывод занимает несколько строк на узел, а для $b+$c он выглядит так:
5 TYPE = add ===> 6
TARG = 1
FLAGS = (SCALAR,KIDS)
{
TYPE = null ===> (4)
(was rv2sv)
FLAGS = (SCALAR,KIDS)
{
3 TYPE = gvsv ===> 4
FLAGS = (SCALAR)
GV = main::b
}
}
{
TYPE = null ===> (5)
(was rv2sv)
FLAGS = (SCALAR,KIDS)
{
4 TYPE = gvsv ===> 5
FLAGS = (SCALAR)
GV = main::c
}
} Это дерево имеет 5 узлов (по одному на каждый спецификатор TYPE), только 3 из них не оптимизированы (по одному на каждое число в левом столбце). Непосредственные дочерние узлы данного узла соответствуют парам {} на одном уровне отступа, таким образом, эта информация соответствует дереву:
add
/ \
null null
| |
gvsv gvsv Порядок выполнения указывается метками ===>, следовательно, это 3 4 5 6 (узел 6 не включён в вышеприведённый список), то есть gvsv gvsv add whatever.
Каждый из этих узлов представляет собой операцию (op), фундаментальную операцию внутри ядра Perl. Код, реализующий каждую операцию, можно найти в файлах pp*.c; функция, реализующая операцию с типом gvsv, это pp_gvsv, и так далее. Как показывает дерево выше, разные операции имеют разное количество дочерних элементов: add — это бинарный оператор, как можно было ожидать, и поэтому имеет два дочерних элемента. Для адаптации к различным количествам дочерних элементов существуют различные типы структур данных операторов, и они связываются различными способами.
Простейший тип структуры оператора — OP: у него нет дочерних элементов. Унарные операторы, UNOP, имеют один дочерний элемент, и на него указывает поле op_first. Бинарные операторы (BINOP) имеют не только поле op_first, но и поле op_last. Наиболее сложный тип оператора — LISTOP, который имеет любое количество дочерних элементов. В этом случае на первый дочерний элемент указывает поле op_first, а на последний — поле op_last. Дочерние элементы между ними можно найти, итеративно следуя указателю OpSIBLING от первого дочернего элемента к последнему (но см. ниже).
Также есть и другие типы операторов: PMOP хранит регулярное выражение и не имеет дочерних элементов, а у LOOP могут или могут не быть дочерние элементы. Если поле op_children отлично от нуля, оно ведет себя как LISTOP. Чтобы усложнить задачу, если UNOP на самом деле является оператором null после оптимизации (см. "Компиляция: этап 2 — распространение контекста"), у него все еще будут дочерние элементы в соответствии с его прежним типом.
Наконец, существует LOGOP, или логический оператор. Как и LISTOP, он имеет один или несколько дочерних элементов, но у него нет поля op_last: поэтому вам нужно следовать указателю op_first, а затем самой цепочке OpSIBLING, чтобы найти последний дочерний элемент. Вместо этого у него есть поле op_other, которое сравнимо с полем op_next, описанным ниже, и представляет собой альтернативный путь выполнения. Операторы, такие как and, or и ?, являются LOGOP. Обратите внимание, что в общем случае op_other может не указывать ни на один из непосредственных дочерних элементов LOGOP.
Начиная с версии 5.21.2, в перлах, скомпилированных с экспериментальной опцией -DPERL_OP_PARENT, добавляется дополнительный булевый флаг для каждого оператора — op_moresib. Когда он не установлен, это указывает, что это последний оператор в цепочке OpSIBLING. Это освобождает поле op_sibling у последнего элемента, чтобы он указывал обратно на родительский оператор. В этой сборке это поле также переименовано в op_sibparent, чтобы отразить его двойную роль. Макрос OpSIBLING(o) оборачивает это специальное поведение и всегда возвращает NULL для последнего элемента. В этой сборке функция op_parent(o) может быть использована для поиска родителя любого оператора. Таким образом, для обеспечения обратной совместимости вы всегда должны использовать макрос OpSIBLING(o) вместо прямого доступа к op_sibling.
Другой способ изучить дерево — использовать модуль компилятора заднего плана, такой как B::Concise.
Этап 1 компиляции: процедуры проверки
Дерево создается компилятором, когда код yacc подает ему конструкции, которые он распознает. Поскольку yacc работает снизу вверх, аналогично происходит и первый проход компиляции Perl.
Для разработчиков Perl интересным в этом этапе является то, что некоторые оптимизации могут быть выполнены на этом этапе. Это оптимизации с помощью так называемых «проверок». Соответствие между именами узлов и соответствующими процедурами проверки описано в файле opcode.pl (не забудьте запустить make regen_headers, если вы изменяете этот файл).
Процедура проверки вызывается, когда узел полностью построен, за исключением потока порядка выполнения. Поскольку на этом этапе нет обратных ссылок на текущий узел, можно выполнить практически любую операцию с узлом верхнего уровня, включая его освобождение и/или создание новых узлов над/под ним.
Процедура проверки возвращает узел, который должен быть вставлен в дерево (если узел верхнего уровня не был изменен, процедура проверки возвращает свой аргумент).
Согласно соглашению, процедуры проверки имеют имена ck_*. Обычно они вызываются из подпрограмм new*OP (или convert) (которые, в свою очередь, вызываются из perly.y).
Этап 1a компиляции: сворачивание констант
Сразу после вызова процедуры проверки возвращаемый узел проверяется на возможность выполнения во время компиляции. Если он (значение считается константой), он сразу же выполняется, и вместо него подставляется узел constant со "значением возврата" соответствующего поддерева. Поддерево удаляется.
Если сворачивание констант не было выполнено, создается поток порядка выполнения.
Этап 2 компиляции: распространение контекста
Когда контекст части дерева компиляции известен, он распространяется вниз по дереву. В данный момент контекст может принимать 5 значений (вместо 2 для контекста времени выполнения): void, boolean, scalar, list и lvalue. В отличие от этапа 1, этот этап обрабатывается сверху вниз: контекст узла определяет контекст его дочерних элементов.
На этом этапе выполняются дополнительные оптимизации, зависящие от контекста. Поскольку на этом моменте дерево компиляции содержит обратные ссылки (через указатели "потока"), узлы не могут быть освобождены (free()). Чтобы разрешить удаление оптимизированных узлов на данном этапе, такие узлы вместо освобождения (free()) нулевые (null()) (т. е. их тип изменяется на 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 — корень дерева операторов, представляющего область действия; это двойной указатель, поэтому вы можете заменить оператор, если нужно.
-
void bhk_post_end(pTHX_ OP **o) -
Этот вызов выполняется в конце лексической области действия, непосредственно после разматывания стека. o, как указано выше. Обратите внимание, что вызовы
pre_иpost_endмогут вложены, если в стеке сохранения есть вызов string eval. -
void bhk_eval(pTHX_ OP *const o) -
Этот вызов выполняется непосредственно перед началом компиляции
eval STRING,do FILE,requireилиuseпосле подготовки eval. o — оператор, который запросил eval, и обычно он будетOP_ENTEREVAL,OP_DOFILEилиOP_REQUIRE.
После того, как у вас есть функции крючков, вам нужна структура BHK, чтобы поместить их туда. Лучше всего выделить ее статически, так как освободить ее нельзя после регистрации. Указатели на функции следует вставлять в эту структуру с помощью макроса BhkENTRY_set, который также установит флаги, указывающие, какие записи допустимы. Если вам нужно выделять BHK динамически по какой-либо причине, обязательно обнулите его перед началом.
После регистрации механизма отключения этих крючков нет, поэтому, если это необходимо, вам нужно будет сделать это самостоятельно. Запись в %^H, вероятно, лучший способ, так что эффект ограничен областью действия; однако также можно использовать макросы BhkDISABLE и BhkENABLE для временного включения и выключения записей. Также следует знать, что, как правило, по крайней мере одна область действия будет открыта до загрузки вашего расширения, поэтому вы увидите пары pre/post_end, у которых не было соответствующей пары start.
Просмотр внутренних структур данных с помощью функций dump
Для помощи в отладке в исходном файле dump.c содержится ряд функций, которые генерируют форматированный вывод внутренних структур данных.
Наиболее часто используемой из этих функций является Perl_sv_dump; она используется для вывода данных SVs, AVs, HVs и CVs. Модуль Devel::Peek вызывает sv_dump для генерации отладочного вывода из пространства Perl, поэтому пользователи этого модуля уже должны быть знакомы с его форматом.
Perl_op_dump можно использовать для вывода структуры OP или её производных, и выводит результат, похожий на perl -Dx; на самом деле, Perl_dump_eval выведет главный корень кода, выполняемого на основе -Dx.
Другие полезные функции — Perl_dump_sub, которая преобразует GV в дерево операций, Perl_dump_packsubs, которая вызывает Perl_dump_sub для всех подпрограмм в пакете следующим образом: (К счастью, все это xsubs, поэтому дерева операций нет)
(gdb) print Perl_dump_packsubs(PL_defstash)
SUB attributes::bootstrap = (xsub 0x811fedc 0)
SUB UNIVERSAL::can = (xsub 0x811f50c 0)
SUB UNIVERSAL::isa = (xsub 0x811f304 0)
SUB UNIVERSAL::VERSION = (xsub 0x811f7ac 0)
SUB DynaLoader::boot_DynaLoader = (xsub 0x805b188 0) и Perl_dump_all, которая выводит все подпрограммы в хранилище и дерево операций главного корня.
Как поддерживаются несколько интерпретаторов и параллельность
Обзор и PERL_IMPLICIT_CONTEXT
Интерпретатор Perl можно рассматривать как закрытый ящик: он имеет API для подачи кода или выполнения других действий, но также имеет функции для собственного использования. Это очень похоже на объект, и есть способ построить Perl так, чтобы иметь несколько интерпретаторов, причём один интерпретатор представлен либо как структура C, либо внутри структуры, специфичной для потока. Эти структуры содержат весь контекст, состояние этого интерпретатора.
Макрос, который управляет основным вариантом сборки Perl, — MULTIPLICITY. Вариант сборки MULTIPLICITY имеет структуру C, которая упаковывает все состояние интерпретатора. При включённой возможности множественности (multiplicity) в Perl, также обычно определён PERL_IMPLICIT_CONTEXT, что позволяет передавать скрытый первый аргумент, представляющий все три структуры данных. MULTIPLICITY делает возможным создание многопоточных интерпретаторов (с моделью потоков ithreads, связанной с макросом USE_ITHREADS.)
Чтобы определить наличие неконстантных данных, можно использовать совместимый с BSD (или GNU) nm:
nm libperl.a | grep -v ' [TURtr] ' Если это отобразит какие-либо D или d символы (или, возможно, C или c), у вас есть неконстантные данные. Символы, которые grep удалил, следующие: Tt — это текст или код, Rr — это только для чтения (константные) данные, а U — <undefined>, внешние символы, на которые ссылаются.
Тест t/porting/libperl.t выполняет проверку целостности символов подобного рода для libperl.a.
Всё это, очевидно, требует способа, чтобы внутренние функции Perl были либо подпрограммами, принимающими какой-либо тип структуры в качестве первого аргумента, либо подпрограммами, не принимающими первый аргумент. Для обеспечения этих двух совершенно разных способов построения интерпретатора исходный код Perl (как и во многих других ситуациях) активно использует макросы и соглашения об именовании подпрограмм.
Первая проблема: определение того, какие функции будут общедоступными функциями API, а какие — закрытыми. Все функции, имена которых начинаются с S_, являются закрытыми (подумайте "S" как "secret" или "static"). Все остальные функции начинаются с "Perl_", но просто потому, что функция начинается с "Perl_", это не означает, что она является частью API. (См. "Внутренние функции".) Наиболее надёжный способ убедиться, что функция является частью API, — найти её запись в perlapi. Если она есть в perlapi, то она является частью API. Если её нет, и вы считаете, что она должна быть (т.е., вам она нужна для вашего расширения), отправьте запрос на https://github.com/Perl/perl5/issues, объяснив, почему вы считаете, что она должна быть.
Вторая проблема: должна быть синтаксическая конструкция, позволяющая одними и теми же объявлениями и вызовами подпрограмм передавать структуру в качестве первого аргумента или ничего не передавать. Для решения этой проблемы подпрограммы именуются и объявляются определённым образом. Вот типичный фрагмент статической функции, используемой внутри Perl:
STATIC void
S_incline(pTHX_ char *s) STATIC превращается в "static" на C и может быть #define'd в пустоту в некоторых конфигурациях в будущем.
Общедоступная функция (т.е. часть внутреннего API, но необязательно разрешённая для использования в расширениях) начинается так:
void
Perl_sv_setiv(pTHX_ SV* dsv, IV num) pTHX_ — это один из многих макросов (в perl.h), которые скрывают детали контекста интерпретатора. THX означает "поток", "это" или "вещь", в зависимости от ситуации. (И нет, Джордж Лукас не участвует. :-) Первый символ может быть 'p' для pрототипа, 'a' для aргумента или 'd' для dекларации, поэтому у нас есть pTHX, aTHX и dTHX, и их варианты.
Когда Perl компилируется без опций, устанавливающих PERL_IMPLICIT_CONTEXT, первый аргумент, содержащий контекст интерпретатора, отсутствует. Конечная нижняя черта в макросе pTHX_ указывает, что расширение макроса требует запятой после аргумента контекста, поскольку следуют другие аргументы. Если PERL_IMPLICIT_CONTEXT не определён, pTHX_ будет проигнорирован, и подпрограмма не будет прототипирована для приёма дополнительного аргумента. Форма макроса без конечной нижней черты используется, когда дополнительных явных аргументов нет.
Когда одна внутренняя функция Perl вызывает другую, она должна передать контекст. Это обычно скрывается с помощью макросов. Рассмотрим sv_setiv. Оно расширяется примерно так:
#ifdef PERL_IMPLICIT_CONTEXT
#define sv_setiv(a,b) Perl_sv_setiv(aTHX_ a, b)
/* can't do this for vararg functions, see below */
#else
#define sv_setiv Perl_sv_setiv
#endif Это хорошо работает и означает, что авторы XS могут с радостью написать:
sv_setiv(foo, bar); и всё это будет работать во всех режимах, с которыми мог быть скомпилирован Perl.
Однако это не работает так чисто для функций с переменным числом аргументов, так как макросы подразумевают, что количество аргументов известно заранее. Вместо этого нам либо нужно их полностью перечислить, передавая aTHX_ в качестве первого аргумента (ядро Perl, как правило, делает это с функциями типа Perl_warner), либо использовать контекстно-независимую версию.
Контекстно-независимая версия Perl_warner называется Perl_warner_nocontext и не принимает дополнительный аргумент. Вместо этого она выполняет dTHX;, чтобы получить контекст из локального хранилища потока. Мы делаем это #define warner Perl_warner_nocontext, чтобы обеспечить совместимость исходного кода для расширений в ущерб производительности. (Передача аргумента дешевле, чем его извлечение из локального хранилища потока.)
При просмотре заголовков/источников Perl можно игнорировать [pad]THXx. Они предназначены только для внутреннего использования в ядре. Расширениям и программам-вкладчикам необходимо знать только [pad]THX.
Что случилось с dTHR?
dTHR был введён в perl 5.005 для поддержки более старой модели потоков. Более старая модель потоков теперь использует механизм THX для передачи указателей контекста, поэтому dTHR больше не нужен. Perl 5.6.0 и более поздние версии всё ещё имеют его для обратной совместимости исходного кода, но он определён как бесполезная операция.
Как использовать всё это в расширениях?
Когда Perl скомпилирован с PERL_IMPLICIT_CONTEXT, расширения, вызывающие любые функции Perl API, должны как-то передавать начальный аргумент контекста. Суть в том, что вам нужно написать это так, чтобы расширение всё равно компилировалось, когда Perl не был скомпилирован с включённым PERL_IMPLICIT_CONTEXT.
Существует три способа сделать это. Во-первых, простой, но неэффективный способ, который также является по умолчанию, чтобы сохранить обратную совместимость с расширениями: всякий раз, когда включается XSUB.h, он переопределяет макросы aTHX и aTHX_ для вызова функции, которая вернёт контекст. Таким образом, что-то вроде:
sv_setiv(sv, num); в вашем расширении будет переведено в это, когда PERL_IMPLICIT_CONTEXT включён:
Perl_sv_setiv(Perl_get_context(), sv, num); или в это в противном случае:
Perl_sv_setiv(sv, num); Вам не нужно ничего нового в вашем расширении, чтобы получить это; поскольку библиотека Perl предоставляет Perl_get_context(), всё будет работать.
Во-вторых, более эффективный способ — использовать следующий шаблон для вашего Foo.xs:
#define PERL_NO_GET_CONTEXT /* we want efficiency */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
STATIC void my_private_function(int arg1, int arg2);
STATIC void
my_private_function(int arg1, int arg2)
{
dTHX; /* fetch context */
... call many Perl API functions ...
}
[... etc ...]
MODULE = Foo PACKAGE = Foo
/* typical XSUB */
void
my_xsub(arg)
int arg
CODE:
my_private_function(arg, 10); Обратите внимание, что единственные два изменения по сравнению с обычным способом написания расширения — это добавление #define PERL_NO_GET_CONTEXT перед включением заголовков Perl, а затем объявление dTHX; в начале каждой функции, которая будет вызывать 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 из нескольких потоков?
Если вы создаёте интерпретаторы в одном потоке, а затем вызываете их в другом, вам нужно убедиться, что собственный слот Thread Local Storage (TLS) Perl инициализирован правильно в каждом из этих потоков.
Функции API perl_alloc и perl_clone автоматически установят слот TLS для созданного ими интерпретатора, так что не нужно ничего особенного, если к интерпретатору всегда обращаются в том же потоке, который его создал, и этот поток не создавал или не вызывал другие интерпретаторы после этого. Если это не так, вам нужно установить слот TLS потока перед вызовом любых функций Perl API в этом конкретном интерпретаторе. Это делается путём вызова макроса PERL_SET_CONTEXT в этом потоке, как первое действие:
/* do this before doing anything else with some_perl */
PERL_SET_CONTEXT(some_perl);
... other Perl API calls on some_perl go here ... Планы на будущее и PERL_IMPLICIT_SYS
Так же, как PERL_IMPLICIT_CONTEXT предоставляет способ объединить всё, что интерпретатор знает о себе, и передать это, так же планируется позволить интерпретатору объединить всё, что он знает об окружающей среде, в которой он работает. Это поддерживается макросом PERL_IMPLICIT_SYS. В настоящее время он работает только с USE_ITHREADS в Windows.
Это позволяет предоставить дополнительный указатель (называемый "окружением хоста") для всех системных вызовов. Это позволяет всем системным вещам поддерживать собственное состояние, разбитое на семь структур C. Это тонкие оболочки вокруг обычных системных вызовов (см. win32/perllib.c) для исполняемого файла Perl по умолчанию, но для более амбициозного хоста (такого, который бы эмулировал fork()) вся дополнительная работа, необходимая для имитации того, что разные интерпретаторы на самом деле являются разными "процессами", будет выполнена здесь.
Двигатель/интерпретатор Perl и хост — ортогональные сущности. В одном процессе может быть один или несколько интерпретаторов и один или несколько "хостов", с произвольными связями между ними.
Внутренние функции
Все внутренние функции Perl, которые будут доступны внешнему миру, снабжены префиксом Perl_, чтобы они не конфликтовали с функциями XS или функциями, используемыми в программе, в которой встроен Perl. Аналогично, все глобальные переменные начинаются с PL_. (По соглашению, статические функции начинаются с S_.)
В ядре Perl (PERL_CORE определено), вы можете получить доступ к функциям с префиксом Perl_ или без него благодаря набору определений, находящихся в файле embed.h. Обратите внимание, что код расширения не должен устанавливать PERL_CORE; это раскрывает внутренности Perl и, вероятно, вызовет сбои в XS при каждом обновлении Perl.
Файл embed.h генерируется автоматически из файлов embed.pl и embed.fnc. embed.pl также создаёт заголовочные файлы для прототипирования внутренних функций, генерирует документацию и многое другое. Важно, что при добавлении новой функции в ядро или изменении существующей функции, вы также должны изменить данные в таблице в файле embed.fnc. Вот пример записи из этой таблицы:
Apd |SV** |av_fetch |AV* ar|I32 key|I32 lval Первый столбец содержит набор флагов, второй — тип возвращаемого значения, а третий — имя функции. Столбцы после этого — аргументы. Флаги задокументированы в верхней части файла embed.fnc.
Если вы редактируете файлы embed.pl или embed.fnc, вам необходимо запустить make regen_headers, чтобы заново сгенерировать файлы embed.h и другие сгенерированные автоматически файлы.
Форматированный вывод IV, UV и NV
Если вы выводите IV, UV или NV вместо форматирования stdio(3), таких как %d, %ld, %f, для обеспечения переносимости следует использовать следующие макросы:
IVdf IV in decimal
UVuf UV in decimal
UVof UV in octal
UVxf UV in hexadecimal
NVef NV %e-like
NVff NV %f-like
NVgf NV %g-like Они будут обрабатывать 64-битные целые числа и длинные двойные числа. Например:
printf("IV is %" IVdf "\n", iv); IVdf будет расширяться до соответствующего формата для IV. Обратите внимание, что пробелы вокруг формата необходимы в случае компиляции кода с C++, чтобы сохранить совместимость со стандартом.
Обратите внимание, что существуют различные типы «длинных двойных чисел»: Perl будет использовать тот, что доступен в компиляторе.
Если вы выводите адреса указателей, используйте %p или UVxf в сочетании с PTR2UV().
Форматированный вывод SVs
Содержимое SVs можно вывести, используя формат SVf, например:
Perl_croak(aTHX_ "This croaked because: %" SVf "\n", SVfARG(err_msg)) где err_msg — это SV.
Не все типы скалярных значений могут быть выведены. Простые типы, безусловно, могут быть выведены: IV, UV, NV или PV. Также, если SV является ссылкой на некоторое значение, оно будет распаковано, и значение будет выведено, или будет отображена информация о типе этого значения и его адресе. Результаты вывода любого другого типа SV не определены и, вероятно, приведут к аварийному завершению интерпретатора. NVs выводятся с помощью формата, похожего на %g.
Обратите внимание, что пробелы вокруг SVf необходимы в случае компиляции кода с C++, чтобы сохранить совместимость со стандартом.
Обратите внимание, что любой файловый дескриптор, в который происходит вывод в формате UTF-8, должен ожидать входных данных в формате UTF-8, чтобы получить корректные результаты и избежать предупреждений о широких символах. Один из способов сделать это для типичных файловых дескрипторов — вызвать Perl с параметром -C>. (См. «-C [number/list]» в perlrun).
Вы можете использовать это для конкатенации двух скаляров:
SV *var1 = get_sv("var1", GV_ADD);
SV *var2 = get_sv("var2", GV_ADD);
SV *var3 = newSVpvf("var1=%" SVf " and var2=%" SVf,
SVfARG(var1), SVfARG(var2)); Форматированный вывод строк
Если вы хотите вывести только байты в строке с нулевым завершением 7-битного формата, можно использовать %s (при условии, что все они действительно 7-битные). Но если есть вероятность, что значение будет закодировано в UTF-8 или содержит байты больше, чем 0x7F (и, следовательно, 8-битные), следует вместо этого использовать формат UTF8f. В качестве параметра используйте макрос UTF8fARG():
chr * msg;
/* U+2018: \xE2\x80\x98 LEFT SINGLE QUOTATION MARK
U+2019: \xE2\x80\x99 RIGHT SINGLE QUOTATION MARK */
if (can_utf8)
msg = "\xE2\x80\x98Uses fancy quotes\xE2\x80\x99";
else
msg = "'Uses simple quotes'";
Perl_croak(aTHX_ "The message is: %" UTF8f "\n",
UTF8fARG(can_utf8, strlen(msg), msg)); Первый параметр UTF8fARG — логическое значение: 1, если строка в UTF-8; 0, если строка в кодировке нативных байтов (Latin1). Второй параметр — количество байтов строки для вывода. Третий и последний параметр — указатель на первый байт в строке.
Обратите внимание, что любой файловый дескриптор, в который происходит вывод в формате UTF-8, должен ожидать входных данных в формате UTF-8, чтобы получить корректные результаты и избежать предупреждений о широких символах. Один из способов сделать это для типичных файловых дескрипторов — вызвать Perl с параметром -C>. (См. «-C [number/list]» в perlrun).
Форматированный вывод Size_t и SSize_t
Наиболее общий способ — привести их к UV или IV и вывести, как описано в предыдущем разделе.
Но если вы используете PerlIO_printf(), то использование модификатора длины %z (для siZe) сократит написание и визуальный шум:
PerlIO_printf("STRLEN is %zu\n", len); Этот модификатор не является переносимым, поэтому его использование должно быть ограничено PerlIO_printf().
Форматированный вывод 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.
Поддержка Юникода
В Perl 5.6.0 была добавлена поддержка Юникода. Важно для портеров и авторов XS понимать эту поддержку и убедиться, что написанный ими код не повреждает данные Юникода.
Что такое Юникод?
В прежние, менее просвещённые времена мы все использовали ASCII. По большей части. Главная проблема с ASCII — то, что он американский. Ну, нет, это не проблема; проблема в том, что он не очень полезен для людей, не использующих латинский алфавит. Раньше различные языки добавляли свои собственные алфавиты в верхнюю часть последовательности, между 128 и 255. Конечно, у нас появилось множество вариантов, которые не были чистым ASCII, и вся суть стандарта была потеряна.
Ещё хуже, если у вас язык, подобный китайскому или японскому, с сотнями или тысячами символов, то вы не сможете поместить их в всего лишь 256, поэтому им пришлось забыть об ASCII и создать свои собственные системы, используя пары чисел для ссылки на один символ.
Чтобы исправить это, некоторые люди сформировали Unicode, Inc. и создали новый набор символов, содержащий все символы, которые вы можете себе представить, и ещё больше. Существует несколько способов представления этих символов, и тот, который используется в Perl, называется UTF-8. UTF-8 использует переменное количество байтов для представления символа. Вы можете узнать больше о Юникоде и модели Юникода Perl в perlunicode.
(На платформах EBCDIC Perl вместо этого использует UTF-EBCDIC, который является формой UTF-8, адаптированной для платформ EBCDIC. Ниже мы говорим только о UTF-8. UTF-EBCDIC похож на UTF-8, но детали отличаются. Макросы скрывают эти различия от вас, просто помните, что конкретные числа и битовые шаблоны, представленные ниже, будут отличаться в UTF-EBCDIC.)
Как определить строку UTF-8?
Вы не можете. Это потому, что данные UTF-8 хранятся в байтах так же, как и данные, не являющиеся UTF-8. Символ Юникода 200 (0xC8 для вас, любителей шестнадцатеричных чисел) — прописная буква «Е» с диакритическим знаком «grave» — представлена двумя байтами v196.172. К сожалению, строка, не являющаяся Юникодом, chr(196).chr(172), также имеет эту последовательность байтов. Поэтому вы не можете определить это просто посмотрев — именно это делает ввод Юникода интересной проблемой.
В общем случае, вы должны либо знать, с чем имеете дело, либо сделать предположение. Функция API is_utf8_string может помочь; она расскажет вам, содержит ли строка только допустимые символы UTF-8, и вероятность того, что строка, не являющаяся UTF-8, будет выглядеть как допустимая UTF-8, быстро уменьшается с увеличением длины строки. В случае проверки посимвольно, isUTF8_CHAR расскажет вам, является ли текущий символ в строке допустимым символом UTF-8.
Как UTF-8 представляет символы Юникода?
Как упоминалось выше, UTF-8 использует переменное число байтов для хранения символа. Символы со значениями 0...127 хранятся в одном байте, как и в хорошем старом ASCII. Символ 128 хранится как v194.128; это продолжается до символа 191, который представляет собой v194.191. Теперь у нас закончились биты (191 в двоичной форме 10111111), поэтому мы переходим дальше; символ 192 представляет собой v195.128. И так далее, переходя к трём байтам на символе 2048. "Кодировки Unicode" в perlunicode содержит изображения, показывающие, как это работает.
Предполагая, что вы знаете, что имеете дело со строкой UTF-8, вы можете узнать длину первого символа в ней с помощью макроса UTF8SKIP:
char *utf = "\305\233\340\240\201";
I32 len;
len = UTF8SKIP(utf); /* len is 2 here */
utf += len;
len = UTF8SKIP(utf); /* len is 3 here */ Другой способ пропустить символы в строке UTF-8 — использовать utf8_hop, который принимает строку и количество символов для пропуска. Однако вы сами отвечаете за проверку границ, поэтому не используйте его легкомысленно.
Все байты в многобайтовом символе UTF-8 будут иметь установленный старший бит, поэтому вы можете проверить, нужно ли выполнить какие-либо специальные действия с этим символом следующим образом (UTF8_IS_INVARIANT() — это макрос, проверяющий, закодирован ли байт как одиночный байт даже в UTF-8):
U8 *utf; /* Initialize this to point to the beginning of the
sequence to convert */
U8 *utf_end; /* Initialize this to 1 beyond the end of the sequence
pointed to by 'utf' */
UV uv; /* Returned code point; note: a UV, not a U8, not a
char */
STRLEN len; /* Returned length of character in bytes */
if (!UTF8_IS_INVARIANT(*utf))
/* Must treat this as UTF-8 */
uv = utf8_to_uvchr_buf(utf, utf_end, &len);
else
/* OK to treat this character as a byte */
uv = *utf; Вы также можете видеть в этом примере, что мы используем utf8_to_uvchr_buf для получения значения символа; обратная функция uvchr_to_utf8 доступна для преобразования UV в UTF-8:
if (!UVCHR_IS_INVARIANT(uv))
/* Must treat this as UTF8 */
utf8 = uvchr_to_utf8(utf8, uv);
else
/* OK to treat this character as a byte */
*utf8++ = uv; Вы обязаны преобразовывать символы в UV с помощью указанных выше функций, если вам когда-либо придётся работать с совпадениями символов UTF-8 и не-UTF-8. В этом случае вы не можете пропустить символы UTF-8. Если вы это сделаете, вы потеряете возможность сопоставить символы не-UTF-8 с высоким битом; например, если ваша строка UTF-8 содержит v196.172, и вы пропустите этот символ, вы никогда не сможете сопоставить chr(200) в строке не-UTF-8. Поэтому не делайте этого!
(Обратите внимание, что в приведенных выше примерах нам не нужно проверять инвариантные символы. Функции работают с любым правильно сформированным вводом UTF-8. Просто быстрее избежать накладных расходов функций, когда это не нужно.)
Как Perl хранит строки UTF-8?
В настоящее время Perl обрабатывает строки UTF-8 и не-UTF-8 несколько по-разному. Флаг в SV, SVf_UTF8, указывает, что строка внутренне закодирована как UTF-8. Без него значение байта является кодовой точкой и наоборот. Этот флаг имеет смысл только в том случае, если SV является SvPOK или сразу после строкового преобразования с помощью SvPV или аналогичного макроса. Вы можете проверить и изменить этот флаг с помощью следующих макросов:
SvUTF8(sv)
SvUTF8_on(sv)
SvUTF8_off(sv) Этот флаг существенно влияет на обработку строк в Perl: если данные UTF-8 не правильно различаются, регулярные выражения, length, substr и другие операции со строками дадут нежелательные (неверные) результаты.
Проблема возникает, когда у вас, например, есть строка, которая не помечена как UTF-8, и содержит последовательность байтов, которая может быть UTF-8 — особенно при объединении строк не-UTF-8 и UTF-8.
Никогда не забывайте, что флаг SVf_UTF8 отделен от значения PV; вам необходимо убедиться, что вы случайно не убрали его во время работы с SV. Более конкретно, вы не можете ожидать сделать следующее:
SV *sv;
SV *nsv;
STRLEN len;
char *p;
p = SvPV(sv, len);
frobnicate(p);
nsv = newSVpvn(p, len); Строка char* не даёт полной информации, и вы не можете скопировать или восстановить SV просто, скопировав значение строки. Проверьте, установлен ли флаг UTF8 в старом SV (после вызова SvPV), и действуйте соответствующим образом:
p = SvPV(sv, len);
is_utf8 = SvUTF8(sv);
frobnicate(p, is_utf8);
nsv = newSVpvn(p, len);
if (is_utf8)
SvUTF8_on(nsv); В приведенном выше примере ваша функция frobnicate была изменена, чтобы понимать, обрабатывает ли она данные UTF-8, чтобы она могла обрабатывать строку должным образом.
Поскольку простое передача SV в XS-функцию и копирование данных SV недостаточно для копирования флагов UTF8, ещё меньше подходит просто передача char * в XS-функцию.
Для полной универсальности используйте макрос DO_UTF8, чтобы увидеть, должна ли строка в SV обрабатываться как UTF-8. Это учитывает, выполняется ли вызов XS-функции в рамках области действия use bytes. В таком случае, подлежащие байты, составляющие строку UTF-8, должны быть представлены, а не символ, который они представляют. Но этот пragma следует использовать только для отладки и, возможно, для низкоуровневого тестирования на уровне байтов. Поэтому большинство кода 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, либо добавив пользовательский оптимизатор «peephole» с модулем 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 для непосредственного помещения элементов в стек временных переменных. Вместо этого для смертности xV используется функция API sv_2mortal(), добавляющая его адрес в стек временных переменных.
Аналогично, нет публичного API для чтения значений из стека временных переменных. Вместо этого используются макросы SAVETMPS и FREETMPS. Макрос SAVETMPS устанавливает базовые уровни стека временных переменных, фиксируя текущее значение PL_tmps_ix в PL_tmps_floor и сохраняя предыдущее значение в стеке сохранения. После этого, всякий раз, когда вызывается FREETMPS, все временные переменные, помещённые после этого уровня, возвращаются.
Хотя эти два макроса часто встречаются парами в паре ENTER/ LEAVE, это не обязательно. Разрешено вызывать FREETMPS несколько раз после последнего вызова SAVETMPS, например, в цикле, перебирающем элементы списка. Хотя вы можете вызывать SAVETMPS несколько раз в паре областей видимости, это маловероятно будет полезно. Последующие вызовы будут перемещать нижнюю границу временных переменных дальше вверх, эффективно оставляя существующие временные переменные для освобождения только в конце области видимости.
Стек сохранения
Стек сохранения используется Perl для реализации ключевого слова local и других аналогичных действий; любые операции очистки, которые необходимо выполнить при выходе из текущего области действия. Элементы, помещенные в этот стек, обычно сохраняют текущее значение некоторой внутренней переменной или состояния, которое будет восстановлено при разворачивании области действия из-за выхода, return, die, goto или по другим причинам.
В то время как другие внутренние стеки Perl хранят отдельные элементы одного и того же типа (обычно указатели SV или целые числа), элементы, помещенные в стек сохранения, формируются из многих различных типов, имеющих несколько полей. Например, типу SAVEt_INT необходимо хранить как адрес переменной int для восстановления, так и значение для её восстановления. Эта информация могла быть сохранена с помощью полей типа struct, но должна была бы быть достаточно большой, чтобы хранить три указателя в самом крайнем случае, что привело бы к потере большого объема памяти в большинстве меньших случаев.
Вместо этого стек хранит информацию в кодировании переменной длины структур ANY. Конечное значение, которое помещено, сохраняется в поле UV, которое кодирует тип элемента, содержащегося в предыдущих элементах; количество и типы которых будут зависеть от того, какой тип элемента сохраняется. Поле типа помещается последним, потому что это будет первое поле, которое будет извлечено при разворачивании элементов из стека.
Основание этого стека указывается переменной интерпретатора PL_savestack типа ANY *.
Вершина стека индексируется PL_savestack_ix, целым числом, которое хранит индекс в массиве, в который должен быть помещен следующий элемент. (Обратите внимание, что это отличается от большинства других стеков, которые ссылаются на последний помещенный элемент).
Элементы помещаются в стек сохранения с помощью различных макросов SAVE...(). Многие из этих макросов принимают переменную и сохраняют как её адрес, так и текущее значение в стеке сохранения, гарантируя, что значение будет восстановлено при выходе из области действия.
SAVEI8(i8)
SAVEI16(i16)
SAVEI32(i32)
SAVEINT(i)
... Также существует множество других макросов специального назначения, которые сохраняют определенные типы или значения, представляющие интерес. SAVETMPS уже упоминалось выше. Другие включают в себя SAVEFREEPV, который организует освобождение PV (то есть буфера строки) или SAVEDESTRUCTOR, которое организует вызов указанного указателя функции при выходе из области действия. Полный список таких макросов можно найти в scope.h.
Нет публичного API для извлечения отдельных значений или элементов из стека сохранения. Вместо этого, с помощью стека области действия, пара ENTER и LEAVE формирует способ начала и остановки вложенных областей действия. Выход из вложенной области действия с помощью LEAVE восстановит все сохраненные значения, которые были помещены после последнего ENTER.
Стек области действия
Как и стек меток со стеком значений, стек области действия образует пару со стеком сохранения. Стек области действия хранит высоту стека сохранения, в которой начинаются вложенные области действия, и позволяет стеку сохранения развернуться до этого момента при выходе из области действия.
Когда Perl собирается с включенными отладкой, в этой стеке есть вторая часть, хранящая имена строк, понятные пользователю, описывающие тип контекста стека. Каждая операция помещения сохраняет имя, а также высоту стека сохранения, а каждая операция извлечения проверяет имя самого верхнего уровня с ожидаемым, что приводит к ошибке утверждения, если имя не совпадает.
Основание этого стека указывается переменной интерпретатора PL_scopestack типа I32 *. При включенной отладке имена стека области действия хранятся в отдельном массиве, на который указывает PL_scopestack_name типа const char **.
Вершина стека индексируется PL_scopestack_ix, целым числом, которое хранит индекс массива или массивов, в который должен быть помещен следующий элемент. (Обратите внимание, что это отличается от большинства других стеков, которые ссылаются на последний помещенный элемент).
Значения помещаются в стек области действия с помощью макроса ENTER, который начинает новую вложенную область действия. Любые элементы, помещенные в стек сохранения, затем восстанавливаются при следующем вложенном вызове макроса LEAVE.
Динамический область действия и стек контекста
Примечание: этот раздел описывает непубличный внутренний API, который может быть изменен без предварительного уведомления.
Введение в стек контекста
В Perl динамическое область действия относится к временному вложению таких вещей, как вызовы подпрограмм, evals и т. д., а также входу и выходу из блоков области действия. Например, восстановление local-переменной определяется динамическим областью действия.
Perl отслеживает динамическое область действия с помощью структуры данных, называемой стеком контекста, который представляет собой массив структур PERL_CONTEXT, который сам по себе является большим объединением всех типов контекста. Всякий раз, когда вводится новая область действия (например, блок, цикл for или вызов подпрограммы), новая запись контекста помещается в стек. Аналогично, при выходе из блока или возврате из вызова подпрограммы и т. д. контекст извлекается. Поскольку стек контекста представляет собой текущее динамическое область действия, его можно искать. Например, next LABEL ищет назад по стеку, ища контекст цикла, который соответствует метке; return извлекает контексты до тех пор, пока не найдет контекст подпрограммы или eval или подобное; caller проверяет контексты подпрограмм в стеке.
Каждая запись контекста имеет метку типа контекста, cx_type. Типичные типы контекстов — CXt_SUB, CXt_EVAL и т. д., а также CXt_BLOCK и CXt_NULL, которые представляют собой базовую область действия (как помещается pp_enter) и блок сортировки. Тип определяет, какие части объединения контекста допустимы.
Основное деление в структуре контекста — между областью действия подстановки (CXt_SUBST) и областями действия блоков, которые представляют собой все остальное. Первый используется только во время выполнения s///e и далее здесь не обсуждается.
Все типы области действия блоков имеют общую базу, что соответствует CXt_BLOCK. Она хранит старые значения различных переменных, связанных с областью действия, таких как PL_curpm, а также информацию о текущей области действия, такую как gimme. При выходе из области действия старые переменные восстанавливаются.
Конкретные типы области действия блоков хранят дополнительную информацию по типу. Например, CXt_SUB хранит текущий выполняемый CV, а различные типы циклов for могут содержать исходную переменную цикла SV. При выходе из области действия данные по типу обрабатываются; например, счётчик ссылок CV уменьшается, а исходная переменная цикла восстанавливается.
Макрос cxstack возвращает основание текущего стека контекста, а cxstack_ix — индекс текущей рамки в этом стеке.
Фактически, стек контекста фактически является частью системы стеков стеков; всякий раз, когда выполняется что-то необычное, например, вызов обработчика DESTROY или tie, то новый стек помещается, а затем извлекается в конце.
Обратите внимание, что API, описанный здесь, значительно изменился в Perl 5.24; до этого использовались большие макросы, такие как PUSHBLOCK и POPSUB; в 5.24 они были заменены описанными ниже встроенными статическими функциями. Кроме того, порядок и детали работы этих макросов/функций изменились во многих отношениях, часто незаметно. В частности, они не обрабатывали сохранение положений стека savestack и временного стека и требовали дополнительных ENTER, SAVETMPS и LEAVE по сравнению с новыми функциями. Макросы старого стиля больше не будут описываться.
Помещение контекстов
Для помещения нового контекста две основные функции — cx = cx_pushblock(), которая помещает новый базовый блок контекста и возвращает его адрес, и семейство подобных функций с именами, например, cx_pushsub(cx), которые заполняют дополнительные поля, зависящие от типа, в структуре cx. Обратите внимание, что CXt_NULL и CXt_BLOCK не имеют собственных функций помещения, так как они не хранят никакие данные помимо тех, которые помещены с помощью cx_pushblock.
Поля структуры контекста и аргументы функций cx_* могут меняться между выпусками Perl, отражая то, что удобно или эффективно для этого выпуска.
Типичный стек контекста, в котором размещается контекст, можно найти в pp_entersub; следующее показывает упрощенный и урезанный пример не-XS вызова, вместе с комментариями, приблизительно показывающими, что делает каждая функция.
dMARK;
U8 gimme = GIMME_V;
bool hasargs = cBOOL(PL_op->op_flags & OPf_STACKED);
OP *retop = PL_op->op_next;
I32 old_ss_ix = PL_savestack_ix;
CV *cv = ....;
/* ... make mortal copies of stack args which are PADTMPs here ... */
/* ... do any additional savestack pushes here ... */
/* Now push a new context entry of type 'CXt_SUB'; initially just
* doing the actions common to all block types: */
cx = cx_pushblock(CXt_SUB, gimme, MARK, old_ss_ix);
/* this does (approximately):
CXINC; /* cxstack_ix++ (grow if necessary) */
cx = CX_CUR(); /* and get the address of new frame */
cx->cx_type = CXt_SUB;
cx->blk_gimme = gimme;
cx->blk_oldsp = MARK - PL_stack_base;
cx->blk_oldsaveix = old_ss_ix;
cx->blk_oldcop = PL_curcop;
cx->blk_oldmarksp = PL_markstack_ptr - PL_markstack;
cx->blk_oldscopesp = PL_scopestack_ix;
cx->blk_oldpm = PL_curpm;
cx->blk_old_tmpsfloor = PL_tmps_floor;
PL_tmps_floor = PL_tmps_ix;
*/
/* then update the new context frame with subroutine-specific info,
* such as the CV about to be executed: */
cx_pushsub(cx, cv, retop, hasargs);
/* this does (approximately):
cx->blk_sub.cv = cv;
cx->blk_sub.olddepth = CvDEPTH(cv);
cx->blk_sub.prevcomppad = PL_comppad;
cx->cx_type |= (hasargs) ? CXp_HASARGS : 0;
cx->blk_sub.retop = retop;
SvREFCNT_inc_simple_void_NN(cv);
*/ Обратите внимание, что cx_pushblock() устанавливает два новых уровня: для стека аргументов (до MARK) и временного стека (до PL_tmps_ix). При выполнении на этом уровне области действия каждый nextstate (среди прочего) сбросит уровни стеков аргументов и tmps до этих уровней. Обратите внимание, что поскольку cx_pushblock использует текущее значение PL_tmps_ix, а не передаёт его как аргумент, это определяет, в какой момент следует вызвать cx_pushblock. В частности, любые новые временные файлы, которые должны быть освобождены только при выходе из области действия (а не при следующем nextstate), должны быть созданы в первую очередь.
Большинство вызывающих функций cx_pushblock просто устанавливают новый уровень стека аргументов на верхнюю часть предыдущей кадровой рамки, но для CXt_LOOP_LIST он сохраняет итерируемые элементы в стеке и, следовательно, устанавливает blk_oldsp на верхнюю часть этих элементов. Обратите внимание, что, вопреки своему названию, blk_oldsp не всегда представляет значение для восстановления PL_stack_sp при выходе из области действия.
Обратите внимание на раннее захват PL_savestack_ix в old_ss_ix, который позже передаётся как аргумент в cx_pushblock. В случае pp_entersub это связано с тем, что, хотя большинство значений, требующих сохранения, хранятся в полях структуры контекста, дополнительное значение необходимо сохранить только при запуске отладчика, и нет смысла раздувать структуру для этого редкого случая. Поэтому оно сохраняется в стеке сохранения. Поскольку это значение вычисляется и сохраняется до помещения контекста, необходимо передать старое значение PL_savestack_ix в cx_pushblock, чтобы гарантировать, что сохранённое значение освобождается при выходе из области действия. Для большинства пользователей cx_pushblock, где ничего не нужно помещать в стек сохранения, PL_savestack_ix просто передаётся непосредственно как аргумент в cx_pushblock.
Обратите внимание, что где это возможно, значения должны быть сохранены в структуре контекста, а не в стеке сохранения; так гораздо быстрее.
Обычно cx_pushblock должен быть немедленно после соответствующего cx_pushfoo, без чего-либо между ними; это потому, что если код между ними может умереть (например, предупреждение, которое стало фатальным), то код разворачивания стека контекста в dounwind увидит (в примере выше) кадр контекста CXt_SUB, но без всех полей, специфичных для подпрограммы, установленных, и вскоре начнутся сбои.
Когда два значения должны быть разделены, первоначально установите тип на CXt_NULL или CXt_BLOCK, а затем измените его на CXt_foo при выполнении cx_pushfoo. Именно это делает pp_enteriter, как только определяется тип цикла, в который он помещается.
Извлечение контекстов
Контексты извлекаются с помощью cx_popsub() и т. д., а также cx_popblock(). Однако, в отличие от cx_pushblock, ни одна из этих функций фактически не уменьшает индекс текущей стека контекстов; это делается отдельно с помощью CX_POP().
Существует два основных способа извлечения контекстов. Во время нормальной работы, когда области видимости выходят из использования, такие функции, как pp_leave, pp_leaveloop и pp_leavesub обрабатывают и извлекают только один контекст с помощью cx_popfoo и cx_popblock. С другой стороны, такие вещи, как pp_return и next, могут потребовать извлечь несколько областей видимости, пока не будет найден контекст подпрограммы или цикла, а исключения (например, die) должны извлечь контексты до тех пор, пока не будет найден контекст вычисления. Оба этих действия выполняются с помощью dounwind(), которая способна обрабатывать и извлекать все контексты, расположенные выше целевого.
Вот типичный пример извлечения контекста, как в pp_leavesub (несколько упрощённый):
U8 gimme;
PERL_CONTEXT *cx;
SV **oldsp;
OP *retop;
cx = CX_CUR();
gimme = cx->blk_gimme;
oldsp = PL_stack_base + cx->blk_oldsp; /* last arg of previous frame */
if (gimme == G_VOID)
PL_stack_sp = oldsp;
else
leave_adjust_stacks(oldsp, oldsp, gimme, 0);
CX_LEAVE_SCOPE(cx);
cx_popsub(cx);
cx_popblock(cx);
retop = cx->blk_sub.retop;
CX_POP(cx);
return retop; Вышеупомянутые шаги выполняются в очень определённом порядке, разработанном как обратный порядок помещения контекста. Сначала необходимо скопировать и/или защитить все возвращаемые аргументы и освободить все временные переменные в текущей области видимости. Выходы из областей видимости, такие как подпрограмма rvalue, обычно возвращают смертную копию своих возвращаемых аргументов (в отличие от подпрограмм lvalue). Важно создать эту копию до извлечения записей из стека сохранения или восстановления переменных, иначе могут произойти такие негативные последствия:
sub f { my $x =...; $x } # $x freed before we get to copy it
sub f { /(...)/; $1 } # PL_curpm restored before $1 copied Хотя мы хотели бы освободить все временные переменные одновременно, мы должны быть осторожны, чтобы не освободить временные переменные, которые поддерживают возвращаемые аргументы живыми; и не освободить временные переменные, которые мы только что создали, копируя возвращаемые аргументы. К счастью, leave_adjust_stacks() способна создавать смертные копии возвращаемых аргументов, смещая аргументы вниз по стеку и обрабатывая только те записи в стеке временных переменных, которые безопасно сделать.
В контексте void не возвращаются аргументы, поэтому эффективнее пропустить вызов leave_adjust_stacks(). Кроме того, в контексте void оператор nextstate, скорее всего, будет немедленно вызван, что выполнит FREETMPS, поэтому в этом нет необходимости.
Следующим шагом является извлечение записей из стека сохранения: CX_LEAVE_SCOPE(cx) просто определён как LEAVE_SCOPE(cx->blk_oldsaveix). Обратите внимание, что во время извлечения Perl может вызывать деструкторы, вызывать STORE для отмены локализации привязанных переменных и так далее. Любой из этих элементов может завершиться сбоем или вызвать exit(). В этом случае будет вызвано dounwind(), и текущая кадр стека контекстов будет повторно обработана. Поэтому крайне важно, чтобы все шаги при извлечении контекста поддерживали возможность повторного входа.
CX_LEAVE_SCOPE сам по себе безопасно поддерживает многократный вход: если только часть элементов стека сохранения была извлечена до сбоя и попадания в ловушку eval, то CX_LEAVE_SCOPE в dounwind или pp_leaveeval продолжат работу с того места, где остановился первый.
Следующим шагом является обработка контекста, специфичного для типа; в данном случае это cx_popsub. Частично это выглядит так:
cv = cx->blk_sub.cv;
CvDEPTH(cv) = cx->blk_sub.olddepth;
cx->blk_sub.cv = NULL;
SvREFCNT_dec(cv); где он обрабатывает только что выполненный CV. Обратите внимание, что перед уменьшением счётчика ссылок CV он обнуляет blk_sub.cv. Это означает, что при повторном входе CV не будет освобождён дважды. Это также означает, что вы не можете полагаться на то, что такие поля, специфичные для типа, будут иметь полезные значения после возврата из cx_popfoo.
Далее, cx_popblock восстанавливает все различные переменные интерпретатора до их предыдущих значений или предыдущих максимальных значений; это расширяется до:
PL_markstack_ptr = PL_markstack + cx->blk_oldmarksp;
PL_scopestack_ix = cx->blk_oldscopesp;
PL_curpm = cx->blk_oldpm;
PL_curcop = cx->blk_oldcop;
PL_tmps_floor = cx->blk_old_tmpsfloor; Обратите внимание, что он не восстанавливает PL_stack_sp; как уже упоминалось ранее, значение, которое необходимо восстановить, зависит от типа контекста (в частности, for (list) {}) и возвращаемых аргументов (если таковые имеются); и это уже будет отсортировано ранее функцией leave_adjust_stacks().
Наконец, указатель стека контекстов фактически уменьшается функцией CX_POP(cx). После этого момента текущий кадр стека контекстов может быть перезаписан другими помещёнными контекстами. Хотя такие вещи, как привязки и DESTROY, должны работать в новом контексте стека, лучше не предполагать этого. В действительности, в отладочных сборках, CX_POP(cx) намеренно устанавливает cx в null для обнаружения кода, который всё ещё полагается на значения полей в этом кадре контекста. Обратите внимание в примере pp_leavesub() выше, что мы получаем blk_sub.retop перед вызовом CX_POP.
Повторное выполнение контекстов
Наконец, есть cx_topblock(cx), которая действует как супер-nextstate по отношению к сбросу различных переменных до их базовых значений. Она используется в таких местах, как pp_next, pp_redo и pp_goto, где вместо выхода из области видимости мы хотим переинициализировать область видимости. Помимо сброса PL_stack_sp, как и nextstate, она также сбрасывает PL_markstack_ptr, PL_scopestack_ix и PL_curpm. Обратите внимание, что она не выполняет FREETMPS.
Выделение операторов на основе блоков
Примечание: этот раздел описывает закрытый внутренний API, который может быть изменён без предварительного уведомления.
Внутренние механизмы обработки ошибок Perl реализуют die (и его внутренние аналоги) с помощью longjmp. Если это происходит во время лексического анализа, синтаксического анализа или компиляции, мы должны убедиться, что все операторы, выделенные в рамках процесса компиляции, освобождены. (Более ранние версии Perl не обрабатывали эту ситуацию должным образом: при неудачной разборке они утекали операторы, которые хранились во внутренних переменных C auto и нигде больше не были связаны.)
Для обработки этой ситуации Perl использует блоки операторов, которые прикреплены к текущему компилируемому CV. Блок — это фрагмент выделенной памяти. Новые операторы выделяются как области в блоке. Если блок заполняется, создаётся новый (и связывается с предыдущим). При возникновении ошибки и освобождении CV все оставшиеся операторы освобождаются.
Каждый оператор предваряется двумя указателями: один указывает на следующий оператор в блоке, а другой указывает на блок, которому он принадлежит. Указатель на следующий оператор необходим, чтобы Perl мог перебирать блок и освобождать все его операторы. (Структуры операторов имеют различный размер, поэтому операторы блока не могут просто рассматриваться как плотный массив.) Указатель на блок необходим для доступа к счётчику ссылок на блок: когда последний оператор в блоке освобождается, сам блок освобождается.
Выделение операторов в блоке происходит в конце блока. Это имеет тенденцию выделять листья дерева операторов в первую очередь, и это, надеюсь, благоприятно для кэша. Кроме того, это означает, что нет необходимости хранить размер блока (см. ниже, почему блоки различаются по размеру), потому что Perl может следовать указателям, чтобы найти последний оператор.
Может показаться возможным полностью исключить счётчики ссылок на блоки, сделав все операторы неявным образом прикреплёнными к PL_compcv при выделении и освобождении при освобождении CV. Это также позволило бы op_free пропустить FreeOp и, следовательно, освободить операторы быстрее. Но это не работает в тех случаях, когда операторы должны сохраняться за пределами своих CV, таких как повторные вычисления.
CV также должен иметь счётчик ссылок на блок. Иногда первый созданный оператор немедленно освобождается. Если счётчик ссылок на блок достигает 0, то он будет освобождён, даже если CV всё ещё указывает на него.
CV использует флаг CVf_SLABBED, чтобы указать, что CV имеет счётчик ссылок на блок. Когда этот флаг установлен, к блоку можно получить доступ через CvSTART, когда CvROOT не установлен, или вычитанием двух указателей (2*sizeof(I32 *)) из CvROOT, когда он установлен. Альтернативой этому подходу по внедрению блока в CvSTART во время компиляции было бы увеличение размера структуры xpvcv ещё на один указатель. Но это сделало бы все CV больше, даже несмотря на то, что освобождение операторов на основе блоков, как правило, полезно только для программ, которые активно используют строковые вычисления.
Когда флаг CVf_SLABBED установлен, CV берёт на себя ответственность за освобождение блока. Если CvROOT не установлен при освобождении или удалении CV, предполагается, что произошла ошибка компиляции, поэтому происходит обход блока операторов, и все операторы освобождаются.
В нормальных обстоятельствах CV забывает о своём блоке (уменьшая счётчик ссылок) при присоединении корня. Таким образом, подсчёт ссылок на блоки, выполняемый при освобождении операторов, обрабатывает освобождение блока. В некоторых случаях CV получает указание забыть о блоке (cv_forget_slab), именно чтобы операторы могли сохраниться после завершения работы CV.
Забывание блока при присоединении корня не строго необходимо, но это предотвращает потенциальные проблемы с переписыванием CvROOT. Везде есть код, как в ядре, так и в CPAN, который выполняет действия с CvROOT, поэтому забывание блока повышает надёжность и предотвращает потенциальные проблемы.
Поскольку CV берёт на себя владение своим блоком при установке флага, этот флаг никогда не копируется при клонировании CV, так как один CV может освободить блок, на который другой CV всё ещё указывает, так как принудительное освобождение операторов игнорирует счётчик ссылок (но проверяет, что он выглядит правильно).
Для предотвращения фрагментации блока освобождённые операторы помечаются как освобождённые и прикрепляются к цепочке освобождённых элементов блока (идея позаимствована из DBM::Deep). Эти освобождённые операторы повторно используются при возможности. Неиспользование освобождённых операторов было бы проще, но это привело бы к значительно большему использованию памяти для программ с большими блоками if (DEBUG) {...}.
SAVEFREEOP немного проблематичен в этой схеме. Иногда он может привести к освобождению оператора после его CV. Если CV принудительно освободил операторы в своём блоке и сам блок, то мы будем работать с освобождённым блоком. Преобразование SAVEFREEOP в пустую операцию не помогает, так как иногда оператор может быть сохранён при отсутствии ошибки компиляции, поэтому оператор никогда не будет освобождён. Он хранит счётчик ссылок на блок, поэтому весь блок будет утечкой. Поэтому SAVEFREEOP теперь устанавливает специальный флаг в операторе (->op_savefree). Принудительное освобождение операторов после ошибки компиляции не освобождает отмеченные операторы.
Поскольку многие фрагменты кода создают крошечные подпрограммы, состоящие только из нескольких операторов, а огромный блок был бы значительным бременем для них, первый блок всегда очень небольшой. Чтобы избежать выделения слишком большого количества блоков для одного CV, каждый последующий блок вдвое больше предыдущего.
Smartmatch ожидает возможность выделения op во время выполнения, его выполнения и последующего удаления. Для этого op просто выделяется с помощью malloc, когда PL_compcv ещё не настроен. Поэтому все выделенные из кучи ops помечаются как таковые (->op_slabbed), чтобы отличить их от выделенных с помощью malloc.
АВТОРЫ
До мая 1997 года этот документ поддерживался Джеффом Окамото <okamoto@corp.hp.com>. Сейчас он поддерживается как часть самого Perl командой Perl 5 Porters <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.34.0/perlguts