Skip to content

Commit 421264f

Browse files
committed
fix: real fixes for 5 macOS doc-test failures + soup link (v0.5.868)
Closes the v0.5.853 exclude list by fixing the underlying bugs: 1. state-desugar: `.value` / `.set` / `.get` come through HIR as NativeMethodCall (object: Some, class_name varies) instead of the PropertyGet shape the rewrite matched. The unrewritten reads stayed on the let-undefined binding, surfacing as TypeError: undefined.length on textarea.ts and TypeError: undefined.slice on state/snippets.ts. Added a NativeMethodCall arm to try_rewrite_state_access that accepts class_name None | Some("State"). 2. WebView/Image option-bag handlers sit *after* the perry/ui catch-all bail at line 1159 — neither method is in perry_ui_table_lookup so the bail fired first ("'WebView' is not a known function (args: 1)"). Added both names to the bail's exclusion list so control reaches the explicit handlers. 3. module_has_symbol only matched entry.name == name; class_filter was invisible to the #463 unimplemented gate. `ethers.Wallet` failed even though createRandom is registered with class_filter=Wallet. Extended the lookup to also accept methods whose class_filter equals the requested name. 4. Re-removed the 5 macOS filter-exclude entries from test.yml's doc-tests cmd_exclude_gallery line (added v0.5.853 as workarounds). 5. link.rs: -lwebkitgtk-6.0 / -ljavascriptcoregtk-6.0 / -lsoup-3.0 when linking perry-ui-gtk4 (was failing with `soup_check_version undefined reference`, the v0.5.864/865 fixes only handled the webview.rs API change but missed the link line). All 5 doc-tests pass locally. No more excludes.
1 parent 2b073cd commit 421264f

5 files changed

Lines changed: 102 additions & 11 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -615,14 +615,7 @@ jobs:
615615
- os: macos-14
616616
ui_backend: perry-ui-macos
617617
shell: bash
618-
# Excludes beyond ui/gallery.ts: 5 doc-tests that fail on CI
619-
# for reasons tracked separately —
620-
# - stdlib/crypto/snippets.ts: ethers.Wallet unimplemented (#463)
621-
# - ui/state/snippets.ts: TypeError: undefined.slice (real bug)
622-
# - ui/tray/snippets.ts: CGSConnectionByID fails on headless CI
623-
# - ui/webview/snippets.ts: WebView 1-arg HIR gap
624-
# - ui/widgets/textarea.ts: TypeError: undefined.length (real bug)
625-
cmd_exclude_gallery: "./scripts/run_doc_tests.sh --verbose --skip-xcompile --filter-exclude ui/gallery.ts --filter-exclude stdlib/crypto/snippets.ts --filter-exclude ui/state/snippets.ts --filter-exclude ui/tray/snippets.ts --filter-exclude ui/webview/snippets.ts --filter-exclude ui/widgets/textarea.ts"
618+
cmd_exclude_gallery: "./scripts/run_doc_tests.sh --verbose --skip-xcompile --filter-exclude ui/gallery.ts"
626619
cmd_gallery: "./scripts/run_doc_tests.sh --verbose --skip-xcompile --filter ui/gallery.ts"
627620
# Repeat `--xcompile-only-target=…` per target rather than a
628621
# single comma-delimited value because PowerShell splits even

crates/perry-api-manifest/src/lib.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,25 @@ pub enum ApiSource {
176176
/// (#463).
177177
pub fn module_has_symbol(module: &str, name: &str) -> Option<&'static ApiEntry> {
178178
let module = module.strip_prefix("node:").unwrap_or(module);
179-
API_MANIFEST
180-
.iter()
181-
.find(|e| e.module == module && e.name == name)
179+
// Match either:
180+
// - a top-level export by name (`ethers.parseEther` → entry.name = parseEther)
181+
// - any method whose class_filter is the requested name (`ethers.Wallet`
182+
// → some entry has Method { class_filter: Some("Wallet") }). Without
183+
// this branch, `ethers.Wallet.createRandom()` failed the #463
184+
// unimplemented gate even though `createRandom` was registered with
185+
// class_filter=Wallet.
186+
API_MANIFEST.iter().find(|e| {
187+
if e.module != module {
188+
return false;
189+
}
190+
if e.name == name {
191+
return true;
192+
}
193+
matches!(
194+
e.kind,
195+
ApiKind::Method { class_filter: Some(c), .. } if c == name
196+
)
197+
})
182198
}
183199

184200
/// True if `path` resolves to a Perry-implemented native module.

crates/perry-codegen/src/lower_call/native.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,11 @@ pub(crate) fn lower_native_method_call(
11611161
&& method != "App"
11621162
&& method != "VStack"
11631163
&& method != "HStack"
1164+
// Image + WebView have option-bag handlers further down that
1165+
// do their own arg destructuring; they're not in perry_ui_table
1166+
// so they must skip this catch-all bail.
1167+
&& method != "Image"
1168+
&& method != "WebView"
11641169
{
11651170
if let Some(sig) = perry_ui_table_lookup(method) {
11661171
return lower_perry_ui_table_call(ctx, sig, args);

crates/perry-transform/src/state_desugar.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,43 @@ fn try_rewrite_state_access(e: &Expr, bindings: &HashMap<LocalId, StateBinding>)
871871
}
872872
}
873873
}
874+
// HIR's "perry/ui state class" lowering routes `state.set(v)` /
875+
// `state.value` / `state.get()` / `state.text()` through
876+
// `NativeMethodCall { module: "perry/ui", class_name: Some("State"),
877+
// object: Some(LocalGet(state_id)), method: ..., args }` instead of
878+
// the PropertyGet shape. Recognize that form here so the rewrite
879+
// actually fires (otherwise `state.value` stays as a no-op method
880+
// call on `undefined` — see textarea / state docs failures).
881+
if let Expr::NativeMethodCall {
882+
module,
883+
class_name,
884+
object: Some(obj),
885+
method,
886+
args,
887+
..
888+
} = e
889+
{
890+
// `.set` carries `class_name: Some("State")`; `.value` / `.get`
891+
// come through with `class_name: None`. Accept either as long
892+
// as the receiver resolves to one of our bindings.
893+
let class_ok = class_name.is_none() || class_name.as_deref() == Some("State");
894+
if module == "perry/ui" && class_ok {
895+
if let Expr::LocalGet(state_id) = obj.as_ref() {
896+
if let Some(binding) = bindings.get(state_id) {
897+
return match method.as_str() {
898+
"get" | "value" if args.is_empty() => {
899+
Some(state_get_call(&binding.synth_id))
900+
}
901+
"set" if args.len() == 1 => {
902+
Some(state_set_call(&binding.synth_id, args[0].clone()))
903+
}
904+
"text" if args.is_empty() => Some(state_text_call(binding)),
905+
_ => None,
906+
};
907+
}
908+
}
909+
}
910+
}
874911
None
875912
}
876913

crates/perry/src/commands/compile/link.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,46 @@ pub(super) fn build_and_run_link(
15211521
);
15221522
cmd.arg("-lshumate-1.0");
15231523
}
1524+
// WebKitGTK 6.0 + libsoup-3.0 — perry/ui WebView (#658, v0.5.864).
1525+
// perry-ui-gtk4's webkit6/soup3 deps reference symbols like
1526+
// `soup_check_version` from libsoup-3.0 transitively; without
1527+
// explicit `-lsoup-3.0` ld errors with `DSO missing from
1528+
// command line`. Same pkg-config → hardcoded-fallback shape
1529+
// as GTK4 / GStreamer / shumate above.
1530+
let mut got_webkit_libs = false;
1531+
let webkit_pc_out = Command::new("pkg-config")
1532+
.args(["--libs", "webkitgtk-6.0", "libsoup-3.0"])
1533+
.output();
1534+
if let Ok(ref output) = webkit_pc_out {
1535+
if output.status.success() {
1536+
let libs = String::from_utf8_lossy(&output.stdout);
1537+
for flag in libs.trim().split_whitespace() {
1538+
cmd.arg(flag);
1539+
}
1540+
got_webkit_libs = true;
1541+
}
1542+
}
1543+
if !got_webkit_libs {
1544+
eprintln!(
1545+
"Warning: `pkg-config --libs webkitgtk-6.0 libsoup-3.0` \
1546+
did not return WebKitGTK linker flags ({}). Falling \
1547+
back to a hardcoded set — install `libwebkitgtk-6.0-dev` \
1548+
(Debian/Ubuntu) which pulls libsoup-3.0-dev + \
1549+
libjavascriptcoregtk-6.0-dev to silence this warning.",
1550+
match &webkit_pc_out {
1551+
Err(e) => format!("pkg-config not runnable: {e}"),
1552+
Ok(o) if !o.status.success() => format!(
1553+
"pkg-config exited {}: {}",
1554+
o.status.code().unwrap_or(-1),
1555+
String::from_utf8_lossy(&o.stderr).trim()
1556+
),
1557+
Ok(_) => "no output".to_string(),
1558+
}
1559+
);
1560+
for lib in ["-lwebkitgtk-6.0", "-ljavascriptcoregtk-6.0", "-lsoup-3.0"] {
1561+
cmd.arg(lib);
1562+
}
1563+
}
15241564
} else if is_windows {
15251565
// Win32 system libs already linked above
15261566
} else {

0 commit comments

Comments
 (0)