Auto-Update, un Inspector Live e il Compilatore che si è Ridotto a Metà
Il post precedente si è chiuso a v0.5.306 sulla storia gen-GC + JSON + benchmark. Quattro giorni dopo, Perry è a v0.5.359 — sono 53 patch release — e la storia è ancora un'altra. Nessuna di quelle release è un titolo a colpi di numeri di benchmark. Quasi tutte sono issue del tracker che vengono chiuse.
perry/updaterarriva — auto-update stile Sparkle/Tauri per app desktop (Ed25519 su un digest SHA-256, sentinel-rollback, riavvio detached). PR community di TheHypnoo (#224).- Geisterhand Fase D — un inspector live a
http://localhost:7676con albero dei widget, dettaglio per widget, dispatch dei click ed editing di stile live viaPOST /style/:h. - Il refactor del compilatore. Tra v0.5.329 → v0.5.343 i quattro file più citati sono stati spezzati:
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%). Il nuovowalker.rstrasforma la classe di bug del catch-all_ =>in errore di compilazione. - Lo styling UI Fase C chiude — props inline
style: { ... }su ogni widget tra Apple, Android, GTK4, Windows e Web. Windows ottiene 4 stub su 5 cablati (decoration / opacity / borders); resta solowidget.shadow(follow-up con DirectComposition). - Un bucket Scoop per Windows:
scoop install perry-ts/perry. Sidecar SHA-256 nel workflow di release. - Ondata di fix di issue dalla community — circa 30 issue chiuse tra runtime, codegen, fetch, GTK4, linker Windows, async e stdlib.
1. perry/updater — auto-update per app desktop
Prima del fix, Perry non aveva un percorso di aggiornamento. Le app uscivano, e basta. TheHypnoo ha aperto #224 con tutta la storia:
import { initUpdater, checkForUpdate, markHealthy } from "@perry/updater";
initUpdater(); // sentinel-rollback se il lancio precedente è crashato
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(); // chiamare dopo che la nuova build è partita correttamenteModello di trust: Ed25519 sul digest SHA-256 del file (non sui byte del file — mantiene la verifica economica sui binari grandi). Il manifest è JSON, versionato per schema, una entry per tripla <os>-<arch>. Installazione atomica con backup <exe>.prev, riavvio detached (setsid su Unix, DETACHED_PROCESS su Windows). Il mobile è escluso per design — App Store / Play Store controllano la pipeline di installazione a livello OS.
Due quirk del runtime di Perry sono emersi scrivendo lo smoke test, e sono stati fixati al volo:
response.arrayBuffer()tornava uno stub di soli metadati. Fixato in #232 (sempre TheHypnoo) —js_response_array_bufferora alloca un veroBufferHeadere famemcpydiresp.bodydentro.fs.appendFileSyncscriveva 0 byte. Fixato in #226 — il path di lowering del namespace-import (import * as fs from "fs") non aveva un arm perappendFileSync, e nemmeno il codegen LLVM aveva un arm per la variante HIR. Entrambi cablati.
La documentazione vive in docs/src/updater/overview.md.
2. Geisterhand: inspector live a localhost:7676
Geisterhand è stato l'harness di test UI in-process di Perry — una API HTTP sulla porta 7676 per snapshottare lo stato dei widget e dispatchare click. La Fase D lo trasforma in un inspector stile devtools che si può aprire da qualsiasi browser.
- Step 1 (v0.5.349) —
GET /serve una UI vanilla-JS single-page con albero dei widget, dettaglio per widget (frame, value, raw JSON), auto-refresh da 1,5 s con pause/resume e un bottone «fire onClick». Il codegen pinnaINSPECTOR_HTMLcontro il lazy-load-dead_stripdi macOS perché sopravviva ai release build. - Step 2 (v0.5.350) —
POST /style/:hprende un sacchetto di props JSON e lo applica live. 9 props (backgroundColor,color,borderColor,borderWidth,borderRadius,opacity,padding,hidden,enabled) fluiscono dal thread HTTP → thread principale via la pump-queue esistente. JSON sbagliato → 400; handle sbagliato → 400; le props sconosciute sono filtrate lato server e la response elenca quali sono passate.
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"]}Il dispatcher macOS è cablato; Linux / Windows / iOS / tvOS / visionOS / Android seguono la stessa forma e sono i prossimi.
3. Il refactor del compilatore — spezzare i quattro file più grossi
Cinque issue nel tracker (#167, #169, #212, #214, più una coda lunga) avevano la stessa forma: una nuova variante di Expr aggiunta a ir.rs, ma uno dei quattro walker ad-hoc in lower.rs aveva un catch-all _ => e mis-compilava silenziosamente la nuova variante. Beccarlo a runtime è caro — a volte invisibile, a volte un SIGSEGV sotto SSO.
v0.5.329 ha introdotto crates/perry-hir/src/walker.rs con walk_expr_children / walk_expr_children_mut — match esaustivi su tutte le 178 varianti di Expr, nessun catch-all. Aggiungere una nuova variante senza elencarla qui ora è un errore di compilazione. I quattro consumer (substitute_locals, find_max_local_id::check_expr, collect_local_refs_expr, remap_local_ids_in_expr) sono collassati:
| Funzione | Prima | Dopo | Δ |
|---|---|---|---|
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% |
Totale: −1.830 righe di descent duplicato, sostituite da +1.840 righe di un walker centralizzato — netto piatto, ma la classe di bug è andata.
Quello ha sbloccato il resto. v0.5.331 → v0.5.343 hanno tagliato i quattro monoliti in 14 commit. I numeri di copertina:
| File | Prima | Dopo | Δ |
|---|---|---|---|
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% |
Lo split è atterrato come 19 nuovi sotto-moduli focalizzati: 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, più una nuova crate crates/perry-dispatch diventata l'unica fonte di verità per le tabelle di metodi UI / system / i18n (il fan-out _ => "perry_ui_unknown" che provocava le sorprese «compila su macOS, si rompe sul web» dell'issue #191 ora è un solo lookup).
I win di perf di Tier 4 hanno accompagnato (v0.5.335–v0.5.336):
- Fusi due passi in
inline_functionse tre passi rayon incompile.rs— risparmia 5 scan di modulo + 3 round-trip dello scheduler per compilazione. - Limitato il parse cache di
perry deva 500 entry, eviction FIFO. Prima del fix, una sessione che girava sunode_modulespoteva trattenere 100+ MB di AST SWC. - Parallelizzato il loop di scrittura
.llpost-codegen — wall-time 2–4× più veloce su SSD con 50+ moduli. Arc<I18nTable>invece di clonare la tabella locale per worker.
I test del workspace sono rimasti a 434 passed / 0 failed / 5 ignored in ogni commit; gap test alla baseline 25/28; doc-test alla baseline 80/82.
4. UI styling Fase C, finita
La Fase C era il rollout di style: { ... } inline. Gli step 1–7 hanno chiuso in questa finestra:
- v0.5.305 → v0.5.306 — superficie di tipo
StyleProps+style:inline su Button. - v0.5.307 → v0.5.309 — destructure inline color/padding/shadow su ogni widget tabella, poi VStack / HStack.
- v0.5.310 → v0.5.311 — stringhe hex + gradient +
parseColora runtime per valori dinamici. - v0.5.312 — docs di styling + issue di tracking Windows.
Poi la passata cross-platform:
- GTK4 (#202, #206) — 4 FFI di styling cablati, più 7 FFI mancanti che bloccavano il gate dei doc-test Linux (v0.5.322).
- macOS (v0.5.324) — plumbing dell'ombra
CALayerperwidget.shadow+ infrastruttura visual_test; class-probeset_colorper i widget non-NSTextField. - iOS / tvOS / visionOS (v0.5.346) — Button
color: ...colpivasetTextColor:suUIButton, che non implementa quel selettore; il panic diobjc2attraversava una frontieraextern "C"e il processo si abortiva. Fixato con lo stesso pattern di class-probe di macOS — UIButton ora viene istradato attraversosetTitleColor:forState:UIControlStateNormal. - Windows (v0.5.347) — 4 stub di styling su 5 cablati (
text.decorationvia round-tripLOGFONT,widget.opacityviaWS_EX_LAYERED+SetLayeredWindowAttributes, borders viaSetWindowSubclass+WM_PAINT). Resta solowidget.shadow(serve DirectComposition).
La matrice di styling in docs/src/ui/styling-matrix.md chiude la finestra con Web a 43/43 Wired, Windows a 42/43 Wired, il resto a copertura piena.
5. La passata di correttezza del runtime — issue per issue
Un tema del periodo: ogni miscompile arrivato dal tracker si è trasformato o in un fix o in un errore di compilazione. Highlight:
- #212 (v0.5.323) — i metodi di classe dentro
fnnon potevano catturare local della fn racchiudente. Repro multi-modulo ora combaciano con Node byte per byte. - #214 (v0.5.321 + v0.5.330) — unboxing di string-handle SSO-safe su 7 site con operandi string:
arr.join,arr.toString,obj[stringKey]get/set/delete,string.match(re),process.env[dynKey], input di digest crypto. Prima del fix, ognuno o tornava silenziosamente garbage o faceva SIGSEGV su operandi inline-string. - #221 (v0.5.351) — gli array vuoti
consta livello modulo perdevano le scritturearr[i]=dall'interno delle funzioni. Emerso quandodiscoverLevels()di Bloom-Engine/jump popolavaLEVEL_FILESa livello modulo via index-assign e la schermata di selezione livello veniva vuota. - #233 (v0.5.357) —
Array.pushdall'interno di una funzione async era silenziosamente cappato a 16 elementi quando l'array entrava come parametro. Le funzioni async non vengono inlinate; la realloc tornava un nuovo puntatore che il chiamante non vedeva mai. Fix: installare un puntatore di forwarding alla vecchia posizione a ogni crescita, riusando il meccanismoGC_FLAG_FORWARDEDesistente del GC. - #235 (v0.5.358) — il dispatch di parametri di default dei metodi passava garbage quando i chiamanti omettevano arg in coda. Due contributori: i declare di metodo cross-module hardcodavano 6 double invece di
arity + 1, elower_class_methodnon chiamava affattobuild_default_param_stmts. Emerso infindOne(filter, options = {})di mongodb che si bloccava in silenzio; il fix è uniforme tra dispatch locale e cross-module. - #236 (v0.5.355) — tre bug indipendenti fetch + promise da un solo repro: api.github.com restituiva 403 anonimo (User-Agent di default ora impostato),
.then(console.log)si bloccava per sempre (i callback null non spingevano entry su TASK_QUEUE), ogni rifiuto fetch stampavaUncaught exception: [object Object](*StringHeadernudo NaN-boxato invece di un veroErrorHeader). - #234 (v0.5.359) —
Blobreale con metodi di istanzaarrayBuffer/text/bytes/slice. Prima del fix,await response.blob()tornava uno stub di soli metadati{size, type}. Fix in tre parti atterrato su runtime + HIR + codegen.
Più i piccoli recuperi:
- #181 — strip-dedup potava in eccesso le monomorfizzazioni generiche su Linux + silent-fallback del link GTK4. Fix: sostituire il filtraggio per pattern di nome con confronto di insieme di simboli via
llvm-nm. I membri con anche un solo simbolo unique vengono tenuti.libperry_ui_macos.atagliato 196 → 35 oggetti senza errori di link. - #220 —
secur32.libaggiunto alla riga di link Windows. - #198 — i18n
FormatNumberround-trip FP via Ryū. - #188 — codegen dispatch cablato per i wrapper di formato
perry/i18n. - #189 / #203 — codegen dispatch
perry/plugin. - #190 — widget Canvas attraverso il codegen LLVM.
- #191 — CameraView attraverso il codegen.
- #192 — widget Table attraverso il codegen.
- #193 (parziale) — 11 arm di dispatch di helper stdlib.
- #98 — ricezione in background delle notifiche su iOS + Android (warm-path).
- #106 — fallback deboli per gli hook FFI di game-loop su watchOS.
- #154 — hook di dispose
using/await using. - #167 — alloca degli arg di
js_native_call_methodissata al blocco di entry. - #169 — arm Uint8Array di
substitute_locals. - #226 —
fs.appendFileSynccablato end-to-end (PR community).
6. Windows + Scoop
La storia della toolchain Windows continua a semplificarsi. v0.5.353 ha pinnato clang -target sui build host — clang non-MSVC nel PATH (MinGW / MSYS2 / Anaconda / bundle GNU di Rust) riscriveva silenziosamente l'IR x86_64-pc-windows-msvc di Perry in windows-gnu, e lld-link non riusciva a risolvere il riferimento __main che l'emettitore mingw32 di LLVM inseriva. Il nuovo probe_clang_default_triple esegue clang --version una volta per processo e stampa una sola nota informativa quando il default dell'host è GNU ma stiamo targetando MSVC. Sopprimere con PERRY_NO_CLANG_PROBE=1.
v0.5.345 ha allineato l'ABI perry-ui Win64 con perry-dispatch — tre firme extern di runtime erano andate alla deriva (perry_ui_navstack_create, perry_ui_menu_add_item_with_shortcut, perry_ui_app_set_timer). Sull'ABI Win64 gli arg posizionali interi e float condividono gli indici di slot, quindi un mismatch legge garbage da registri non inizializzati. SysV (macOS / Linux) usa pool di registri int/float separati e per caso atterravano bit validi — crash solo Windows, fixato sulle 8 crate di piattaforma perry-ui-*.
Poi: scoop install perry-ts/perry. Manifest pinnato a v0.5.345 (con depends: main/llvm per tirare automaticamente il LLVM ufficiale default-MSVC). Il workflow di release ora emette sidecar <artifact>.sha256 accanto a ogni archivio, in formato compatibile sha256sum per ogni bumper di 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. Tirando le somme
Il pattern di questo tratto è engagement della community più igiene interna. TheHypnoo ha consegnato tre PR significativi (#224 perry/updater, #231 cablaggio di fs.appendFileSync, #232 byte di body in response.arrayBuffer). Il tracker si è svuotato di circa 30 issue. Il compilatore è diventato 60% più piccolo sul suo file più grosso e ha messo su un walker esaustivo che trasforma «ho dimenticato di aggiornare uno dei quattro walker ad-hoc» da miscompile runtime a errore cargo build. Lo styling UI ha raggiunto la parità su ogni piattaforma desktop tranne le ombre su Windows. Geisterhand ha fatto crescere una superficie devtools da browser. Il path di installazione su Windows si è accorciato di un comando.
Provalo:
# npm (qualunque piattaforma)
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 per app desktop
npm install @perry/updater
# Inspector live
perry compile main.ts -o app --enable-geisterhand
./app & # poi apri http://localhost:7676Source: github.com/PerryTS/perry — Issue: github.com/PerryTS/perry/issues — Changelog: CHANGELOG.md
— Ralph