# pperl 0.6.22 *2026-10-06 to 2026-10-11, 89 commits.* ## Language and runtime - **%Config is complete**: it has every key perl 5.44's has, 1,261 of them, where it had 198. The `d_*` and `i_*` answers come from probing the C library and include tree the way Configure does, the type sizes and formats from the build target, the tools from `PATH`, and the version, signal and extension keys from the running interpreter. `ExtUtils::MakeMaker` no longer warns about uninitialized values on every `WriteMakefile`, `DirHandle`, `Net::protoent` and `Net::servent` no longer believe their functions are missing, and `$Config{PERL_VERSION}`, `$Config{d_readdir}`, `$Config{uidsize}` and the rest exist. - **The library's data files** are in the binary and readable: the files perl's modules open by path beside themselves, such as `ExtUtils::Manifest`'s default `MANIFEST.SKIP` and the `unicore` tables `Unicode::UCD` reads. `make distdir` no longer copies `MYMETA.*` into the distribution directory, and `Unicode::UCD`'s `charblock`, `charscript` and friends no longer depend on a perl installed on the host. `@INC` starts with `./pperl-native`, the embedded tree's own entry. - **The daemon and duplicated output handles**: a script run through the daemon with `PPERL_DAEMON=2` that duplicates `STDOUT`, `STDERR` or `STDIN` while it compiles - every script that loads `Test::More` does - printed its output only on the first run, and the program reading that first run's output never saw it end. Each run now writes to its own output and reads its own input. - **$0 and the environment**: assigning a name longer than the command line to `$0` no longer destroys the environment that child processes inherit. A program that renamed itself and then ran `system`, a backtick or `exec` had its children start with almost no environment (no `HOME`, no `PATH` additions), while its own `%ENV` looked fine. ## JIT and parallelization - **Conditional expressions** in compiled loops: a `?:` whose arm reads an element through a reference (`$c ? $x->[$i] : 0`) or from a hash (`$c ? $h{$k} : 0`) no longer makes the loop leave compiled code each time that arm is taken. An arm that is an element of a plain array already stayed compiled. - **Recursive subs called from compiled loops**: a loop such as `for (1 .. $n) { local $_; $sum += fib($k) }` died with `panic: pp_iter, type=9` once it compiled, when the sub it calls recurses; `Benchmark`'s `timethese` and `countit` of a recursive sub died the same way. The compiled loop mistook a deeper recursion level's frame for its own. - **A regex run once no longer gets compiled**: the regex compiler decided by the size of the string a pattern was matched against, so a pattern used a single time on a large string, as `charnames::viacode` and `vianame` do for every lookup over the one-megabyte names table, was compiled for that one match and the compilation cost several times the match. A place in the program that builds a new pattern for every lookup and runs each once now compiles none of them after the first; a loop of a thousand charnames lookups runs in a fifth of the instructions, and a pattern that is reused compiles when it did. - **Integers past 2^53 in compiled loops**: a loop that computes an integer beyond 9007199254740992 and divides by it next, such as the long division inside `Math::BigInt` on numbers held as floats (`Math::BigFloat->new(2)->bpow(240)->broot(9)` with `Math::BigInt::FastCalc`), could get one quotient digit wrong the first time the loop ran on float-typed values after integer-typed ones; later runs were right. The compiled loop had taken a rounded copy of the exact integer perl kept. It now leaves compiled code for that pass, as it does whenever a value cannot be held exactly. - **$_ in a nested statement-modifier loop**: in a compiled loop that contains `EXPR for LO .. HI`, where EXPR calls a function and reads `$_` in its arguments, the first pass of every inner loop saw `$_` undefined: a "Use of uninitialized value $_" warning, and 0 where the value was wanted, so a range not starting at 0 gave wrong results. ## Modules - **Native modules in the release build**: the build's profile-guided optimisation now runs POSIX, Socket, Errno, Config, Time::Piece, Sys::Syslog, IPC::SysV, the DBM modules, Unicode::Normalize, Unicode::UCD, charnames, the Symbol and EBCDIC encodings, the `:encoding`, `:via` and `:mmap` layers, File::Map, Hash::Util, Clone, Memoize, Math::BigInt::FastCalc, B, Opcode, Safe, PadWalker, Devel::Peek, mro, attributes, source filters, EV, Event, Coro's loop bridges, Peta::Dict, Peta::PCRE2, Peta::XS, Peta::FFI and Peta::LMDB before it decides what is hot. None of these had been run by the build before, so the release binary carried them compiled for size rather than for speed. - **Errno has every name the host defines**: `%!`, the constants and `@Errno::EXPORT_OK` now come from the errno.h of the machine pperl was built on and the headers it includes, read at build time the way perl's own Errno.pm is generated. `EFTYPE`, `EFSBADCRC` and `EFSCORRUPTED`, which current kernel headers define and the previous fixed table lacked, exist; on this host the list is identical to perl 5.44's, all 137 names. - **The -C switch**: `pperl -CSAD script` and the other forms of perl's Unicode switch work, alone and at the end of a cluster such as `-wCSD`; they were refused with "Unknown option: -C", and only `PERL_UNICODE` in the environment had the effect. - **Compress::Raw::Bzip2** exists, with `Compress::Raw::Bunzip2` and the `BZ_*` constants, over the system's libbz2. With it `IO::Compress::Bzip2`, `IO::Uncompress::Bunzip2`, `IO::Uncompress::AnyUncompress` and `Archive::Tar` read and write bzip2 data, `.tbz` archives among it, and `streamzip` makes bzip2 zip entries; they died with "Can't locate Compress/Raw/Bzip2.pm". - **Peta::ReadLine** is the name of the readline module; `Term::ReadLine::Gnu` is an alias of it, so `use Term::ReadLine` still picks it up, a program that tests `$term->ReadLine eq "Term::ReadLine::Gnu"` (CPAN.pm, the debugger) still passes, and `Term::ReadLine::Gnu::XS::*` calls still work. `Peta::ReadLine->new` reports `Peta::ReadLine`, version 0.2.0; the alias reports the name it was asked by. - **Peta::ReadLine's function table and keymaps**: `get_all_function_names` lists the bindable commands (it returned one name of garbage), `get_function_name` finds a function's name, and `$attribs->{executing_keymap}` and `$attribs->{binding_keymap}` are `Keymap` objects that `get_keymap_name` and the binding methods accept. `import Term::ReadLine::Gnu qw(:keymap_type)` and the other export tags work under the alias as they do under `Peta::ReadLine`. - **Peta::ReadLine's ignore_some_completions_function**: a completion with `$attribs->{ignore_some_completions_function}` set no longer dies with "panic: attempt to copy freed scalar" in the statement that called `complete_internal` or `call_function`. - **Peta::ReadLine has its own line editor**: no readline, history or curses library is loaded any more, so line editing works where none is installed and in the static build. It behaves as GNU Readline 8.3 does - commands, key bindings, the init file, history, completion, signals, and the same screen after every key. In a UTF-8 locale the commands move, delete, change case and search by characters, not bytes. It identifies itself: `$attribs->{library_version}` is `8.3 (Peta::ReadLine 0.2.0)` and `$attribs->{gnu_readline_p}` is 0. - **Imager** follows Imager 1.037. `getsamples` checks its arguments itself and says in `errstr` why a call failed (a row outside the image, a channel the image does not have); a failed call returns the empty list, or undef in scalar context. An image too large to allocate fails `Imager->new` with a message instead of ending the program, and `set_out_of_memory_handler` exists. - **Imager drawing and images**: `box`, `line` (plain and anti-aliased) and `arc` draw; `getscanline`, `setscanline`, the palette rows and `setcolors` work; images convert between 8-bit, 16-bit and double samples and can be compared. `open_log`, `log` and `close_log` write Imager's log, and colours have `red`, `green`, `blue`, `alpha` and HSV conversion. Three results changed to what Imager gives: a 16-bit image read as 8-bit colours was one too bright for some values, `i_img_info`'s fourth value is the channel mask, and the colour count of a direct image is undef. - **Imager I/O objects**: `Imager::IO` exists: `new_buffer`, `new_fd`, `new_fh`, `new_cb` and `new_bufchain` (and the `io_new_*` functions) make an I/O object over a string, a file descriptor, a file handle, Perl callbacks or a file in memory, with `read`, `write`, `seek`, `getc`, `gets`, `peekn`, `close` and the rest of the class. `io_slurp` of an object that is not a memory file dies with a message; under Imager itself it ends the program silently. - **Imager PNM files**: Imager reads and writes PNM files (PBM, PGM and PPM, ASCII and binary, 8 and 16 bit), `read_multi` included, so `Imager->new(file => 'x.ppm')` and `$img->write(type => 'pnm')` work. `set_file_limits`, `get_file_limits` and `check_file_limits` bound what is read, and the format of a file is detected from its first bytes, with `add_file_magic` for signatures of your own. - **Imager raw files**: `read` and `write` with `type => 'raw'` work: rows of bare 8-bit samples, interleaved by sample or by line, with `raw_datachannels` and `raw_storechannels` choosing which channels of the file are kept. - **Imager BMP files**: Windows BMP files are read at 1, 4, 8, 16, 24 and 32 bits a pixel, run length encoded and with bit-field masks included, and written uncompressed. A grey image with an alpha channel is written with its own grey; under Imager itself each pixel takes its neighbour's value as alpha and half the row is garbage. - **Imager TGA files**: Targa files are read and written: colour-mapped, direct colour and grey, run length encoded or not, at 8, 15, 16, 24 and 32 bits a pixel, with the `compress`, `wierdpack` and `idstring` options. `%Imager::formats` now lists what Imager lists without any library: raw, pnm, bmp, tga and ifs. - **Imager masked images**: `$img->masked` gives a view of part of an image, optionally written through a mask, that can be drawn into, copied and written to a file. `setsamples` is available, with its floating point and 16-bit forms, and so are `getsamples` with `type => 'float'` and `type => '16bit'`. A floating point sample outside 0 to 1 now wraps when stored in an 8- or 16-bit image, as in Imager, and `getmask` on a fresh image is -1. - **Imager fills**: `box` and `arc` take a `fill`, and `Imager::Fill` makes solid, hatched, image and opacity fills with the twelve combine modes (`normal`, `multiply`, `add`, `darken`, `hue` and the rest). - **Imager paste and crop**: `$img->paste` copies a rectangle of another image in, converting between sample depths and channel counts, and `$img->crop` cuts one out. - **Imager circles and flood fill**: `circle` draws filled anti-aliased circles and, with `filled => 0`, outlines, plain or anti-aliased; `arc` with `filled => 0` draws the outline of an arc. `flood_fill` fills the region around a point with a colour or a fill, optionally up to a `border` colour, and `polybezier` draws a Bezier curve. - **Imager polygons**: `polygon` and `polypolygon` fill anti-aliased polygons with a colour or a fill, by the even-odd or the non-zero winding rule, and `arc` with `aa => 1` draws an anti-aliased pie slice. - **Imager rubthrough and compose**: `$img->rubthrough` lays another image over the image by its alpha channel, and `$img->compose` does so with a combine mode, an `opacity` and optionally a `mask` image. - **Imager fountain fills**: `Imager::Fill->new(fountain => ...)` makes a gradient fill (linear, bilinear, radial, square, revolution or conical, with repeats, segments and super-sampling), and `$img->filter(type => 'fountain')` draws one over a whole image. - **Imager channel operations**: `$img->convert` makes a new image by a matrix or a preset (`grey`, `noalpha`, `rgb`, `addalpha` and the single channels), `Imager->combine` makes one from channels of several images, `$img->map` runs channels through lookup tables, and `$img->flip` mirrors the image. - **Imager quantisation**: `$img->to_paletted` turns a direct colour image into a paletted one, with the palette from median cut, the `addi` method, a fixed map or your own colours, and the pixels matched by closest colour, perturbation or error diffusion. `Imager->make_palette`, `getcolorcount`, `getcolorusage` and `getcolorusagehash` work. - **Imager scaling**: `$img->scale`, `scaleX` and `scaleY` return a scaled copy, with the `normal`, `preview` and `mixing` methods. - **Imager rotation**: `$img->rotate` turns an image by quarter turns (`right`) or by any angle (`degrees`, `radians`, with `back`), and `$img->matrix_transform` maps it through a 3 x 3 matrix. - **Imager trim**: `$img->trim` and `trim_rect` find and remove the border of an image, by colour ranges (`colors`, `auto`) or by transparency (`alpha`). - **Imager transform and transform2**: `Imager::transform2` computes an image from an expression per pixel (`rpnexpr`, or `expr` with Parse::RecDescent), reading up to three input images, and `$img->transform` moves pixels by two stack programs. - **Imager filters**: `$img->filter` has every filter Imager has built in: `autolevels`, `autolevels_skew`, `bumpmap`, `bumpmap_complex`, `contrast`, `gaussian`, `gaussian2`, `gradgen`, `hardinvert`, `hardinvertall`, `mosaic`, `nearest_color`, `noise`, `postlevels`, `radnoise`, `turbnoise`, `unsharpmask` and `watermark`, beside `conv` and `fountain`. `$img->difference` and `rgb_difference` show where two images differ. - **Imager object destructors**: calling the `DESTROY` of an Imager class on something that is not a reference dies with Imager's message, "is not a reference"; it named an expected type instead. `$Imager::_context` exists. - **Imager plugin loader**: `Imager->load_plugin`, which loads filter plugins compiled from C, returns undef and sets `$Imager::ERRSTR` instead of dying with "Undefined subroutine". Such plugins cannot be loaded; Imager has documented that interface as deprecated since 2006. - **Peta::WebGPU and fork**: a process that has opened a device can fork. The child used to hang for ever when it exited; now it ends normally. Using an inherited device, buffer or shader in the child dies with a message that says to open a device there, and the child can do so and compute on it. Where the parent has the `gl` backend open, the child's device comes from the other backends. - **Peta::WebGPU batches**: `$gpu->batch(sub { ... })`, or `$gpu->begin` and `$gpu->commit`, send any number of dispatches to the card in one go, and `$pipeline->bind(\@buffers)` gives an object `run` takes in place of the pipeline and its buffer list, so a recurring dispatch no longer binds its buffers each time. A `write` inside a batch takes effect in order with the dispatches around it, and `$gpu->timed(sub { ... })` returns the nanoseconds the card spent on a batch. `$gpu->features` and `$gpu->has_feature` name the optional features of the device, which now has half-precision floats in shaders (`enable f16;`) and timestamp queries wherever the adapter does. - **Peta::WebGPU buffers**: `$buffer->read($offset, $length)` reads a part of a buffer, so fetching one number no longer copies the whole buffer back from the card. `$buffer->write` takes a PDL ndarray of type `float`, `long` or `ulong` as well as a byte string. - **Peta::WebGPU memory**: a buffer made with contents no longer takes twice its size on the card until the next dispatch; large contents go up in pieces. `$buffer->destroy` frees a buffer's memory at once, `$gpu->memory` reports what the device has allocated, and `Device->new(memory_limit => 90)` turns running out of card memory into an `Out of Memory` exception at 90 percent of the driver's budget. A program that frees buffers and at once makes new ones is no longer told the card is full while the freed memory is still on its way back, and an error message names the first failure, not one that followed from it. - **Peta::WebGPU on OpenGL**: `Device->new(backend => 'gl')` opens on the NVIDIA driver, where it failed with "Parent device is lost". Drawing works there; compute needs OpenGL 4.3 or ES 3.1, and on an older context dies with a message that names the `vulkan` backend. - **Net::SSLeay version constants**: `OPENSSL_VERSION_MAJOR`, `OPENSSL_VERSION_MINOR`, `OPENSSL_VERSION_PATCH` and `OPENSSL_VERSION_NUMBER` name the OpenSSL that is loaded. They kept the values of the release pperl was built beside, so after a system update to OpenSSL 3.6.5 `OPENSSL_VERSION_PATCH` still said 4. - **GD** has the rest of GD 2.91: the affine matrices and transforms, `colorReplace` and its variants, `convolution`, `resolution`, the transposed, rotated, scaled and blurred copies, animated GIF, HEIF and AVIF, 48 functions in all. `GD::VERSION_STRING` is now libgd's version string and `GD::LIBGD_VERSION` its number (2.0303 for libgd 2.3.3), as in GD itself. `GD::Image->supported` and the `newFrom` methods follow what the installed libgd can read. - **GD colorsTotal**: `$image->colorsTotal` on a truecolor image returns `undef`, as in GD itself. It returned 0. - **Config::config_vars** and `-V` print the way Perl's `print` does: to the handle `select` chose, through a tied `STDOUT`, and in order with the program's other output. They wrote to the process's standard output directly, so a capture saw nothing and, on a pipe, their lines could come out ahead of text printed earlier. `Config::compile_date` and the matching line of `-V` read `Compiled at Oct 8 2026 20:42:44`, the layout `Config::Perl::V` parses; they named the compiler instead. - **Cairo, Cwd, Term::ReadLine::Gnu**: `Cairo::HAS_PS_SURFACE`, `HAS_PDF_SURFACE`, `HAS_SVG_SURFACE`, `HAS_RECORDING_SURFACE` and `HAS_FT_FONT` exist. `Cwd::CLONE` exists. `rl_save_state` returns a `readline_state_tPtr` object, which is freed with its last reference and is the only thing `rl_restore_state` accepts.