Auto-update, un inspector live et le compilateur qui s'est réduit de moitié
This dated article records the Perry version and measurements available when it was published. Use the current limitations, platform guide, and benchmark artifact for decisions today.
Le précédent billet s'est arrêté à v0.5.306 sur l'histoire gen-GC + JSON + benchmarks. Quatre jours plus tard, Perry est en v0.5.359 — soit 53 patch releases — et l'histoire est encore différente. Aucune de ces releases n'est un titre à coups de chiffres de benchmark. Presque toutes sont des issues du tracker qui se ferment.
perry/updaterarrive — auto-update façon Sparkle/Tauri pour les apps desktop (Ed25519 sur un digest SHA-256, sentinel-rollback, relancement détaché). PR communautaire de TheHypnoo (#224).- Geisterhand Phase D — un inspector live à
http://localhost:7676avec arbre des widgets, détail par widget, dispatch de clic et édition de style en direct viaPOST /style/:h. - Le refactor du compilateur. Sur v0.5.329 → v0.5.343, les quatre fichiers les plus cités ont été découpés :
lower::lower_expr6 687 → 624 LOC (−91 %),compile.rs9 391 → 3 783 LOC (−60 %),lower.rs13 591 → 7 554 LOC (−44 %),lower_call.rs7 000+ → 4 681 LOC (−33 %). Le nouveauwalker.rstransforme la classe de bug du catch-all_ =>en erreur de compilation. - Le styling UI Phase C boucle — props inline
style: { ... }sur chaque widget, sur Apple, Android, GTK4, Windows et Web. Windows obtient 4 stubs sur 5 câblés (decoration / opacity / borders) ; il ne reste quewidget.shadow(suite à venir avec DirectComposition). - Un bucket Scoop pour Windows :
scoop install perry-ts/perry. Sidecars SHA-256 dans le workflow de release. - Vague de fixes d'issues communautaires — environ 30 issues fermées sur runtime, codegen, fetch, GTK4, linker Windows, async et stdlib.
1. perry/updater — auto-update pour apps desktop
Avant ce fix, Perry n'avait aucune voie de mise à jour. Les apps étaient publiées, et puis voilà. TheHypnoo a ouvert #224 avec toute l'histoire :
import { initUpdater, checkForUpdate, markHealthy } from "@perry/updater";
initUpdater(); // sentinel-rollback si le précédent lancement a crashé
const update = await checkForUpdate({
manifestUrl: "https://example.com/updates/manifest.json",
publicKey: "<ed25519 raw 32-byte hex>",
currentVersion: "1.4.0",
});
if (update) {
await update.download((pct) => console.log(`${pct}%`));
await update.installAndRelaunch();
}
markHealthy(); // à appeler après que le nouveau build a démarré correctementModèle de confiance : Ed25519 sur le digest SHA-256 du fichier (pas sur les octets du fichier — la vérification reste bon marché sur les gros binaires). Le manifest est en JSON, versionné par schéma, une entrée par triplet <os>-<arch>. Installation atomique avec backup <exe>.prev, relancement détaché (setsid sur Unix, DETACHED_PROCESS sur Windows). Le mobile est exclu par design — App Store / Play Store contrôlent l'installation au niveau OS.
Deux particularités du runtime Perry sont apparues en écrivant le smoke test, et ont été fixées dans la foulée :
response.arrayBuffer()renvoyait un stub de métadonnées seules. Fixé dans #232 (encore TheHypnoo) —js_response_array_bufferalloue désormais un vraiBufferHeaderetmemcpyresp.bodydedans.fs.appendFileSyncécrivait 0 octet. Fixé dans #226 — le chemin de lowering namespace-import (import * as fs from "fs") n'avait pas de bras pourappendFileSync, et le codegen LLVM n'avait pas non plus de bras pour la variante HIR. Les deux désormais câblés.
La documentation vit dans docs/src/updater/overview.md.
2. Geisterhand : inspector live à localhost:7676
Geisterhand a été le harnais de tests UI in-process de Perry — une API HTTP sur le port 7676 pour snapshotter l'état des widgets et dispatcher des clics. La Phase D le transforme en inspector style devtools que l'on peut ouvrir depuis n'importe quel navigateur.
- Étape 1 (v0.5.349) —
GET /sert une UI vanilla-JS mono-page avec arbre des widgets, détail par widget (frame, value, raw JSON), auto-refresh de 1,5 s avec pause/reprise, et un bouton « tirer onClick ». Le codegen épingleINSPECTOR_HTMLcontre le lazy-load-dead_stripde macOS pour qu'il survive aux release builds. - Étape 2 (v0.5.350) —
POST /style/:hprend un sac de props JSON et l'applique en direct. 9 props (backgroundColor,color,borderColor,borderWidth,borderRadius,opacity,padding,hidden,enabled) traversent du thread HTTP → thread principal via la pump-queue existante. JSON invalide → 400 ; handle invalide → 400 ; les props inconnues sont filtrées côté serveur et la réponse liste celles qui sont passées.
perry compile main.ts -o app --enable-geisterhand
./app &
open http://localhost:7676
curl -X POST localhost:7676/style/3 \
-H 'content-type: application/json' \
-d '{"backgroundColor":"#1a1a1e","opacity":0.8}'
# => {"ok":true,"applied":["backgroundColor","opacity"]}Le dispatcher macOS est câblé ; Linux / Windows / iOS / tvOS / visionOS / Android suivent le même schéma et sont les prochains.
3. Le refactor du compilateur — découper les quatre plus gros fichiers
Cinq issues du tracker (#167, #169, #212, #214, plus une longue queue) avaient la même forme : une nouvelle variante d'Expr ajoutée à ir.rs, mais l'un des quatre walkers ad-hoc dans lower.rs avait un catch-all _ => et compilait silencieusement la nouvelle variante de travers. Attraper ça à l'exécution coûte cher — parfois invisible, parfois un SIGSEGV sous SSO.
v0.5.329 a introduit crates/perry-hir/src/walker.rs avec walk_expr_children / walk_expr_children_mut — matches exhaustifs sur les 178 variantes d'Expr, pas de catch-all. Ajouter une nouvelle variante sans la lister ici devient une erreur de compilation. Les quatre consommateurs (substitute_locals, find_max_local_id::check_expr, collect_local_refs_expr, remap_local_ids_in_expr) ont fondu :
| Fonction | Avant | Après | Δ |
|---|---|---|---|
find_max_local_id::check_expr | 225 | 57 | −75 % |
substitute_locals | 553 | 80 | −86 % |
collect_local_refs_expr | 720 | 70 | −90 % |
remap_local_ids_in_expr | 542 | 85 | −84 % |
Total : −1 830 lignes de descente dupliquée, remplacées par +1 840 lignes d'un walker centralisé — net plat, mais la classe de bug disparaît.
Cela a débloqué le reste. v0.5.331 → v0.5.343 ont taillé les quatre monolithes sur 14 commits. Les chiffres de couverture :
| Fichier | Avant | Après | Δ |
|---|---|---|---|
lower::lower_expr | 6 687 | 624 | −91 % |
compile.rs | 9 391 | 3 783 | −60 % |
lower.rs | 13 591 | 7 554 | −44 % |
lower_call.rs | 7 000+ | 4 681 | −33 % |
Le découpage a atterri sous forme de 19 nouveaux sous-modules ciblés : compile/{parse_cache, strip_dedup, library_search, object_cache, resolve, collect_modules, optimized_libs, targets, link}.rs, lower/{expr_misc, expr_function, expr_object, expr_call, expr_member, expr_assign, expr_new}.rs, lower_call/{ui_styling, builtin, native}.rs, plus une nouvelle crate crates/perry-dispatch devenue la source unique de vérité pour les tables de méthodes UI / system / i18n (le fan-out _ => "perry_ui_unknown" qui causait les surprises « compile sur macOS, casse sur web » de l'issue #191 est désormais un seul lookup).
Les wins perf de Tier 4 ont accompagné (v0.5.335–v0.5.336) :
- Fusion de deux passes dans
inline_functionset de trois passes rayon danscompile.rs— économise 5 scans de module + 3 allers-retours du scheduler par compilation. - Borné le parse cache de
perry devà 500 entrées, eviction FIFO. Avant le fix, une session traversantnode_modulespouvait retenir plus de 100 Mo d'AST SWC. - Parallélisation de la boucle d'écriture
.llpost-codegen — wall-time 2–4× plus rapide sur SSD avec 50+ modules. Arc<I18nTable>au lieu de cloner la table de locales par worker.
Les tests workspace sont restés à 434 passed / 0 failed / 5 ignored à chaque commit ; gap tests à la baseline 25/28 ; doc-tests à la baseline 80/82.
4. UI styling Phase C, terminée
La Phase C était le rollout de style: { ... } inline. Les étapes 1–7 ont fermé dans cette fenêtre :
- v0.5.305 → v0.5.306 — surface de type
StyleProps+style:inline sur Button. - v0.5.307 → v0.5.309 — destructure inline color/padding/shadow sur chaque widget de table, puis VStack / HStack.
- v0.5.310 → v0.5.311 — chaînes hex + dégradé +
parseColorruntime pour les valeurs dynamiques. - v0.5.312 — docs de styling + issue de tracking Windows.
Puis la passe cross-platform :
- GTK4 (#202, #206) — 4 FFIs de styling câblés, plus 7 FFIs manquants qui bloquaient la porte des doc-tests Linux (v0.5.322).
- macOS (v0.5.324) — plomberie d'ombre
CALayerpourwidget.shadow+ infrastructure visual_test ; sondage de classeset_colorpour les widgets non-NSTextField. - iOS / tvOS / visionOS (v0.5.346) — Button
color: ...tapaitsetTextColor:surUIButton, qui n'implémente pas ce sélecteur ; le panicobjc2traversait une frontièreextern "C"et le processus s'abortait. Fixé via le même pattern de sondage de classe que macOS — UIButton route maintenant viasetTitleColor:forState:UIControlStateNormal. - Windows (v0.5.347) — 4 stubs de styling sur 5 câblés (
text.decorationvia round-tripLOGFONT,widget.opacityviaWS_EX_LAYERED+SetLayeredWindowAttributes, borders viaSetWindowSubclass+WM_PAINT). Il ne reste quewidget.shadow(nécessite DirectComposition).
La matrice de styling dans docs/src/ui/styling-matrix.md termine la fenêtre avec Web à 43/43 Wired, Windows à 42/43 Wired, le reste en couverture complète.
5. La passe de correction du runtime — issue par issue
Un thème de la période : chaque miscompile entré par le tracker s'est terminé en fix ou en erreur de compilation. Les points saillants :
- #212 (v0.5.323) — les méthodes de classe à l'intérieur de
fnne pouvaient pas capturer les locals de la fn englobante. Les repros multi-modules correspondent maintenant à Node octet par octet. - #214 (v0.5.321 + v0.5.330) — unboxing de string-handle SSO-safe sur 7 sites à opérande string :
arr.join,arr.toString,obj[stringKey]get/set/delete,string.match(re),process.env[dynKey], input de digest crypto. Avant le fix, chacun rendait silencieusement du garbage ou faisait SIGSEGV sur des opérandes string inline. - #221 (v0.5.351) — les arrays vides
constau niveau module perdaient les écrituresarr[i]=depuis l'intérieur des fonctions. Apparu quanddiscoverLevels()de Bloom-Engine/jump remplissaitLEVEL_FILESau niveau module via index-assign et l'écran de sélection de niveau s'affichait vide. - #233 (v0.5.357) —
Array.pushdepuis l'intérieur d'une fonction async était silencieusement plafonné à 16 éléments quand l'array entrait en paramètre. Les fonctions async ne sont pas inlinées ; la réallocation rendait un nouveau pointeur que l'appelant ne voyait jamais. Fix : installer un pointeur de forwarding à l'ancienne position à chaque croissance, en réutilisant le mécanismeGC_FLAG_FORWARDEDexistant du GC. - #235 (v0.5.358) — le dispatch des paramètres par défaut de méthode passait du garbage quand les appelants omettaient des args en queue. Deux contributeurs : les declares de méthode cross-module hardcodaient 6 doubles au lieu d'
arity + 1, etlower_class_methodn'appelait pas du toutbuild_default_param_stmts. Apparu dansfindOne(filter, options = {})de mongodb se bloquant en silence ; le fix est uniforme entre dispatch local et cross-module. - #236 (v0.5.355) — trois bugs indépendants fetch + promise depuis un seul repro : api.github.com renvoyait 403 en anonyme (User-Agent par défaut désormais positionné),
.then(console.log)se bloquait pour toujours (les callbacks null ne poussaient pas d'entrées dans la TASK_QUEUE), chaque rejet de fetch imprimaitUncaught exception: [object Object](*StringHeadernu NaN-boxé au lieu d'un vraiErrorHeader). - #234 (v0.5.359) — vrai
Blobavec méthodes d'instancearrayBuffer/text/bytes/slice. Avant le fix,await response.blob()rendait un stub de métadonnées seules{size, type}. Fix en trois parties atterrissant sur runtime + HIR + codegen.
Plus les petits rattrapages :
- #181 — strip-dedup sur-élaguait les monomorphisations génériques sous Linux + silent-fallback du link GTK4. Fix : remplacer le filtrage par pattern de nom par une comparaison d'ensemble de symboles via
llvm-nm. Les membres avec ne serait-ce qu'un symbole unique sont conservés.libperry_ui_macos.aélagué de 196 → 35 objets sans erreurs de link. - #220 —
secur32.libajouté à la ligne de link Windows. - #198 — i18n
FormatNumberround-trip FP via Ryū. - #188 — codegen dispatch câblé pour les wrappers de format
perry/i18n. - #189 / #203 — codegen dispatch
perry/plugin. - #190 — widget Canvas via le codegen LLVM.
- #191 — CameraView via le codegen.
- #192 — widget Table via le codegen.
- #193 (partiel) — 11 bras de dispatch d'helpers stdlib.
- #98 — réception en arrière-plan des notifications sur iOS + Android (warm-path).
- #106 — fallbacks faibles pour les hooks FFI de game-loop watchOS.
- #154 — hooks de dispose
using/await using. - #167 — alloca des args de
js_native_call_methodremontée au bloc d'entrée. - #169 — bras Uint8Array de
substitute_locals. - #226 —
fs.appendFileSynccâblé de bout en bout (PR communautaire).
6. Windows + Scoop
L'histoire de la toolchain Windows continue de se simplifier. v0.5.353 a épinglé clang -target sur les builds host — clang non-MSVC dans le PATH (MinGW / MSYS2 / Anaconda / bundles GNU de Rust) réécrivait silencieusement l'IR x86_64-pc-windows-msvc de Perry vers windows-gnu, et lld-link ne pouvait pas résoudre la référence __main que l'émetteur mingw32 de LLVM insérait. Le nouveau probe_clang_default_triple exécute clang --version une fois par processus et imprime une seule note informative quand le default du host est GNU mais qu'on cible MSVC. Supprimer avec PERRY_NO_CLANG_PROBE=1.
v0.5.345 a aligné l'ABI perry-ui Win64 avec perry-dispatch — trois signatures extern runtime avaient dérivé (perry_ui_navstack_create, perry_ui_menu_add_item_with_shortcut, perry_ui_app_set_timer). Sur ABI Win64, les args positionnels entiers et flottants partagent les indices de slot, donc un mismatch lit du garbage depuis des registres non-initialisés. SysV (macOS / Linux) utilise des pools de registres int/float séparés et faisait par hasard atterrir des bits valides — crash uniquement Windows, fixé sur les 8 crates de plateforme perry-ui-*.
Puis : scoop install perry-ts/perry. Manifest épinglé à v0.5.345 (avec depends: main/llvm pour tirer automatiquement le LLVM officiel default-MSVC). Le workflow de release émet désormais des sidecars <artifact>.sha256 à côté de chaque archive, au format compatible sha256sum pour tout bumper de package manager downstream.
# Host Windows
scoop bucket add perry-ts https://github.com/PerryTS/perry
scoop install perry-ts/perry
perry compile src\main.ts --target windows -o myapp.exe7. Pour conclure
Le motif de cette période est l'engagement communautaire plus l'hygiène interne. TheHypnoo a livré trois PRs significatifs (#224 perry/updater, #231 câblage de fs.appendFileSync, #232 octets de body de response.arrayBuffer). Le tracker s'est vidé d'environ 30 issues. Le compilateur a perdu 60 % sur son plus gros fichier et a poussé un walker exhaustif qui transforme « j'ai oublié de mettre à jour l'un des quatre walkers ad-hoc » d'un miscompile runtime en erreur cargo build. Le styling UI a atteint la parité sur chaque plateforme desktop sauf les ombres sous Windows. Geisterhand a obtenu une surface devtools navigateur. Le chemin d'installation Windows s'est raccourci d'une commande.
Essayez :
# npm (toute plateforme)
npm install @perryts/perry
npx perry compile src/main.ts -o myapp && ./myapp
# Homebrew (macOS)
brew install PerryTS/perry/perry
# Scoop (Windows)
scoop bucket add perry-ts https://github.com/PerryTS/perry
scoop install perry-ts/perry
# Auto-update pour apps desktop
npm install @perry/updater
# Inspector live
perry compile main.ts -o app --enable-geisterhand
./app & # puis ouvrez http://localhost:7676Source : github.com/PerryTS/perry — Issues : github.com/PerryTS/perry/issues — Changelog : CHANGELOG.md
— Ralph
Cet article te plaît ? Reçois le suivant.
De brèves notes sur les releases de Perry et ce qu'on prépare ensuite.
Your email is sent to Resend for the Perry newsletter. You can request unsubscribe or deletion at any time. Privacy notice