Auto-Update, một Inspector trực tiếp và Compiler tự cắt mình một nửa
Bài trước khép lại ở v0.5.306 với câu chuyện gen-GC + JSON + benchmark. Bốn ngày sau, Perry đã ở v0.5.359 — tức 53 patch release — và câu chuyện lại khác. Không có release nào trong số đó là tiêu đề của những con số benchmark. Hầu hết đều là các issue trong tracker được đóng.
perry/updaterđã có — auto-update theo phong cách Sparkle/Tauri cho ứng dụng desktop (Ed25519 trên SHA-256 digest, sentinel-rollback, relaunch tách rời). PR cộng đồng từ TheHypnoo (#224).- Geisterhand Phase D — một inspector trực tiếp tại
http://localhost:7676với cây widget, chi tiết theo từng widget, dispatch click và chỉnh style trực tiếp quaPOST /style/:h. - Refactor compiler. Trong khoảng v0.5.329 → v0.5.343, bốn file được nhắc đến nhiều nhất đã được cắt nhỏ:
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%).walker.rsmới biến lớp bug catch-all_ =>thành lỗi compile. - UI styling Phase C đóng — props inline
style: { ... }trên mọi widget của Apple, Android, GTK4, Windows và Web. Windows được gắn 4/5 stub (decoration / opacity / borders); chỉ cònwidget.shadow(follow-up DirectComposition). - Một bucket Scoop cho Windows:
scoop install perry-ts/perry. Sidecar SHA-256 trong workflow release. - Làn sóng fix issue cộng đồng — khoảng 30 issue được đóng trên runtime, codegen, fetch, GTK4, linker Windows, async và stdlib.
1. perry/updater — auto-update cho ứng dụng desktop
Trước khi fix, Perry không có lộ trình cập nhật. Ứng dụng được phát hành, rồi phát hành nữa, vậy thôi. TheHypnoo mở #224 với toàn bộ câu chuyện:
import { initUpdater, checkForUpdate, markHealthy } from "@perry/updater";
initUpdater(); // sentinel-rollback nếu lần khởi động trước bị 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(); // gọi sau khi build mới khởi động thành côngMô hình tin cậy: Ed25519 trên SHA-256 digest của file (không phải trên byte file — giúp việc verify rẻ ngay cả với binary lớn). Manifest là JSON, có versioning theo schema, mỗi entry cho một bộ ba <os>-<arch>. Cài đặt nguyên tử với backup <exe>.prev, relaunch tách rời (setsid trên Unix, DETACHED_PROCESS trên Windows). Mobile bị loại khỏi thiết kế — App Store / Play Store sở hữu pipeline cài đặt ở mức OS.
Hai điểm lạ của runtime Perry lộ ra khi viết smoke test, và được fix luôn:
response.arrayBuffer()trả về một stub chỉ có metadata. Đã fix trong #232 (cũng TheHypnoo) —js_response_array_buffergiờ allocate mộtBufferHeaderthực vàmemcpyresp.bodyvào trong.fs.appendFileSyncghi 0 byte. Đã fix trong #226 — đường lowering namespace-import (import * as fs from "fs") không có nhánh choappendFileSync, và codegen LLVM cũng chưa có nhánh cho biến thể HIR. Cả hai đều đã được nối.
Tài liệu nằm ở docs/src/updater/overview.md.
2. Geisterhand: inspector trực tiếp tại localhost:7676
Geisterhand vốn là khung kiểm thử UI in-process của Perry — HTTP API trên cổng 7676 để snapshot trạng thái widget và dispatch click. Phase D biến nó thành một inspector kiểu devtools mà bạn có thể mở từ bất kỳ trình duyệt nào.
- Bước 1 (v0.5.349) —
GET /phục vụ một UI vanilla-JS một trang gồm cây widget, chi tiết theo widget (frame, value, raw JSON), auto-refresh 1,5 giây với pause/resume, và một nút «kích hoạt onClick». Codegen ghimINSPECTOR_HTMLchống lại lazy-load-dead_striptrên macOS để nó sống sót qua release build. - Bước 2 (v0.5.350) —
POST /style/:hnhận một túi props JSON và áp dụng trực tiếp. 9 props (backgroundColor,color,borderColor,borderWidth,borderRadius,opacity,padding,hidden,enabled) chảy từ thread HTTP → thread chính qua pump-queue sẵn có. JSON sai → 400; handle sai → 400; props không nhận diện được sẽ bị lọc phía server và response liệt kê những props đã được áp.
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"]}Dispatcher macOS đã được nối; Linux / Windows / iOS / tvOS / visionOS / Android theo cùng hình dạng và sẽ là kế tiếp.
3. Refactor compiler — cắt bốn file lớn nhất
Năm issue trong tracker (#167, #169, #212, #214, cộng thêm phần đuôi dài) cùng dạng: một biến thể Expr mới được thêm vào ir.rs, nhưng một trong bốn walker ad-hoc trong lower.rs có catch-all _ => và lặng lẽ compile sai biến thể mới đó. Bắt cái này khi runtime thì đắt — đôi khi vô hình, đôi khi là SIGSEGV dưới SSO.
v0.5.329 giới thiệu crates/perry-hir/src/walker.rs với walk_expr_children / walk_expr_children_mut — match exhaustive trên cả 178 biến thể Expr, không có catch-all. Thêm một biến thể mới mà không liệt kê ở đây bây giờ là lỗi compile. Bốn người dùng (substitute_locals, find_max_local_id::check_expr, collect_local_refs_expr, remap_local_ids_in_expr) đã co lại:
| Hàm | Trước | Sau | Δ |
|---|---|---|---|
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% |
Tổng: −1.830 dòng descent trùng lặp, được thay bằng +1.840 dòng walker tập trung — net gần như phẳng, nhưng lớp bug đã biến mất.
Điều đó mở khóa phần còn lại. v0.5.331 → v0.5.343 chia tách bốn monolith trong 14 commit. Các con số tiêu đề:
| File | Trước | Sau | Δ |
|---|---|---|---|
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% |
Việc tách hạ cánh dưới dạng 19 sub-module tập trung: 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, cộng thêm crate mới crates/perry-dispatch trở thành nguồn duy nhất của sự thật cho các bảng method UI / system / i18n (cú fan-out _ => "perry_ui_unknown" từng gây các bất ngờ «compile được trên macOS, vỡ trên web» ở issue #191 nay là một cú lookup duy nhất).
Các thắng lợi perf Tier 4 đi cùng (v0.5.335–v0.5.336):
- Gộp hai pass trong
inline_functionsvà ba pass rayon trongcompile.rs— tiết kiệm 5 lần quét module + 3 vòng round-trip scheduler mỗi lần compile. - Giới hạn parse cache của
perry devở 500 entry, FIFO eviction. Trước khi fix, một phiên đi quanode_modulescó thể giữ trên 100 MB AST của SWC. - Song song hóa vòng lặp ghi
.llsau codegen — wall-time nhanh 2–4 lần trên SSD với 50+ module. Arc<I18nTable>thay vì clone bảng locale theo từng worker.
Test workspace giữ ở 434 passed / 0 failed / 5 ignored qua mỗi commit; gap test ở baseline 25/28; doc-test ở baseline 80/82.
4. UI styling Phase C, hoàn tất
Phase C là cuộc rollout của style: { ... } inline. Các bước 1–7 đóng trong cửa sổ này:
- v0.5.305 → v0.5.306 — bề mặt kiểu
StyleProps+style:inline trên Button. - v0.5.307 → v0.5.309 — destructure inline color/padding/shadow trên mọi widget bảng, rồi đến VStack / HStack.
- v0.5.310 → v0.5.311 — chuỗi hex + gradient +
parseColortại runtime cho giá trị động. - v0.5.312 — tài liệu styling + issue tracking Windows.
Sau đó là quét cross-platform:
- GTK4 (#202, #206) — 4 FFI styling được nối, cộng thêm 7 FFI thiếu đang chặn cổng doc-test trên Linux (v0.5.322).
- macOS (v0.5.324) — đường ống bóng
CALayerchowidget.shadow+ hạ tầng visual_test; class-probeset_colorcho widget không phảiNSTextField. - iOS / tvOS / visionOS (v0.5.346) — Button với
color: ...đang gọisetTextColor:trênUIButton, vốn không cài đặt selector đó; panic củaobjc2vượt qua biênextern "C"và process bị abort. Đã fix theo cùng pattern class-probe như macOS — UIButton giờ đi quasetTitleColor:forState:UIControlStateNormal. - Windows (v0.5.347) — 4/5 stub styling được nối (
text.decorationquaLOGFONTround-trip,widget.opacityquaWS_EX_LAYERED+SetLayeredWindowAttributes, borders quaSetWindowSubclass+WM_PAINT). Chỉ cònwidget.shadow(cần DirectComposition).
Ma trận styling trong docs/src/ui/styling-matrix.md kết thúc cửa sổ với Web ở 43/43 Wired, Windows ở 42/43 Wired, phần còn lại đầy đủ.
5. Lượt rà soát đúng đắn của runtime — issue đến issue
Chủ đề của giai đoạn: mọi miscompile vào qua tracker đều biến thành fix hoặc lỗi compile-time. Điểm nhấn:
- #212 (v0.5.323) — method class bên trong
fnkhông thể capture local của fn bao quanh. Repro nhiều module nay khớp byte-cho-byte với Node. - #214 (v0.5.321 + v0.5.330) — unbox string-handle an toàn với SSO trên 7 site có toán hạng string:
arr.join,arr.toString,obj[stringKey]get/set/delete,string.match(re),process.env[dynKey], đầu vào digest crypto. Trước khi fix, mỗi cái hoặc trả rác im lặng hoặc SIGSEGV với toán hạng string inline. - #221 (v0.5.351) — mảng
constrỗng ở mức module bị mất các thao tác ghiarr[i]=từ trong các hàm. Lộ ra khidiscoverLevels()của Bloom-Engine/jump điềnLEVEL_FILESở mức module qua index-assign và màn chọn level hiển thị trống. - #233 (v0.5.357) —
Array.pushtừ trong hàm async im lặng bị giới hạn ở 16 phần tử khi mảng vào dưới dạng tham số. Hàm async không được inline; realloc trả về một con trỏ mới mà người gọi không thấy. Fix: cài đặt một con trỏ forwarding tại vị trí cũ ở mỗi lần lớn lên, tận dụng cơ chếGC_FLAG_FORWARDEDsẵn có của GC. - #235 (v0.5.358) — dispatch tham số mặc định của method truyền rác khi caller bỏ qua các arg cuối. Hai phần đóng góp: declare method cross-module hardcode 6 double thay vì
arity + 1, vàlower_class_methodhoàn toàn không gọibuild_default_param_stmts. Lộ ra qua việcfindOne(filter, options = {})của mongodb treo im lặng; fix đồng nhất giữa dispatch nội bộ và cross-module. - #236 (v0.5.355) — ba bug fetch + promise độc lập từ một repro: api.github.com 403 cho ẩn danh (giờ đặt User-Agent mặc định),
.then(console.log)treo mãi (callback null không đẩy entry vào TASK_QUEUE), mọi reject của fetch inUncaught exception: [object Object](NaN-box*StringHeadertrần thay vìErrorHeaderthật). - #234 (v0.5.359) —
Blobthật với các method instancearrayBuffer/text/bytes/slice. Trước khi fix,await response.blob()trả về stub chỉ có metadata{size, type}. Fix ba phần đáp xuống runtime + HIR + codegen.
Cùng vài cái nhỏ:
- #181 — strip-dedup tỉa quá đà các monomorphization generic trên Linux + silent-fallback link GTK4. Fix: thay lọc theo pattern tên bằng so sánh tập hợp ký hiệu qua
llvm-nm. Member chỉ cần có một ký hiệu riêng cũng được giữ.libperry_ui_macos.ađược tỉa từ 196 → 35 object mà không có lỗi link. - #220 — thêm
secur32.libvào dòng link Windows. - #198 — i18n
FormatNumberround-trip FP qua Ryū. - #188 — nối codegen dispatch cho các wrapper format
perry/i18n. - #189 / #203 — codegen dispatch
perry/plugin. - #190 — widget Canvas qua codegen LLVM.
- #191 — CameraView qua codegen.
- #192 — widget Table qua codegen.
- #193 (một phần) — 11 nhánh dispatch helper stdlib.
- #98 — nhận thông báo nền trên iOS + Android (warm-path).
- #106 — fallback yếu cho hook FFI vòng lặp game trên watchOS.
- #154 — hook dispose
using/await using. - #167 — nâng alloca của args
js_native_call_methodlên block entry. - #169 — nhánh Uint8Array của
substitute_locals. - #226 —
fs.appendFileSyncnối từ đầu đến cuối (PR cộng đồng).
6. Windows + Scoop
Câu chuyện toolchain Windows vẫn đang đơn giản hóa. v0.5.353 ghim clang -target trên các build host — clang không phải MSVC trên PATH (MinGW / MSYS2 / Anaconda / bundle GNU của Rust) đang im lặng viết lại IR x86_64-pc-windows-msvc của Perry thành windows-gnu, và lld-link không thể giải tham chiếu __main mà emitter mingw32 của LLVM chèn vào. probe_clang_default_triple mới chạy clang --version một lần mỗi process và in một dòng ghi chú thông tin khi default của host là GNU nhưng đang target MSVC. Có thể tắt với PERRY_NO_CLANG_PROBE=1.
v0.5.345 căn chỉnh ABI perry-ui Win64 với perry-dispatch — ba chữ ký extern runtime đã lệch (perry_ui_navstack_create, perry_ui_menu_add_item_with_shortcut, perry_ui_app_set_timer). Trên ABI Win64, các arg vị trí kiểu integer và float chia sẻ chỉ số slot, nên một mismatch sẽ đọc rác từ register chưa khởi tạo. SysV (macOS / Linux) dùng pool register int/float tách rời và tình cờ đặt đúng bit hợp lệ — chỉ Windows mới crash, được fix trên cả 8 crate platform perry-ui-*.
Sau đó: scoop install perry-ts/perry. Manifest ghim ở v0.5.345 (với depends: main/llvm để tự kéo về LLVM mặc định MSVC chính thức). Workflow release nay sản xuất các sidecar <artifact>.sha256 bên cạnh từng archive, định dạng tương thích sha256sum cho mọi bumper 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. Khép lại
Khuôn mẫu của giai đoạn này là sự tham gia của cộng đồng cộng với việc dọn dẹp nội bộ. TheHypnoo gửi ba PR đáng kể (#224 perry/updater, #231 nối fs.appendFileSync, #232 byte body của response.arrayBuffer). Tracker giảm khoảng 30 issue. Compiler nhỏ đi 60% trên file lớn nhất và mọc thêm một walker exhaustive biến «quên cập nhật một trong bốn walker ad-hoc» từ một miscompile runtime thành lỗi cargo build. UI styling đạt mức tương đương trên mọi nền desktop, chỉ trừ shadow trên Windows. Geisterhand mọc thêm bề mặt devtools trên trình duyệt. Đường cài đặt trên Windows ngắn đi một lệnh.
Thử ngay:
# npm (mọi nền tảng)
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 cho ứng dụng desktop
npm install @perry/updater
# Inspector trực tiếp
perry compile main.ts -o app --enable-geisterhand
./app & # rồi mở http://localhost:7676Source: github.com/PerryTS/perry — Issues: github.com/PerryTS/perry/issues — Changelog: CHANGELOG.md
— Ralph