Skip to content

fix(ui-windows): AdBanner was an unresolved external - add the placeholder widget#6640

Merged
proggeramlug merged 3 commits into
mainfrom
fix/win-ui-adbanner
Jul 20, 2026
Merged

fix(ui-windows): AdBanner was an unresolved external - add the placeholder widget#6640
proggeramlug merged 3 commits into
mainfrom
fix/win-ui-adbanner

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

AdBanner is a required dispatch-table row (perry-dispatch/src/ui_table/part_a.rs, method \AdBanner\ -> \perry_ui_adbanner_create) but the symbol did not exist in perry-ui-windows: any TS program touching AdBanner failed at link with an unresolved external (found in the 2026-07-18 Windows audit).

This ports the macOS placeholder semantics (perry-ui-macos/src/widgets/adbanner.rs, #867): no ads SDK exists for desktop, so the widget is an empty STATIC pinned via fixed width/height to the Google AdSize slot dimensions (banner/large-banner/medium-rectangle/full-banner/leaderboard, DPI-scaled) — layouts previewed on Windows reserve exactly the space the real ad occupies on mobile.

Verified on Windows 11: the test app compiles + links (previously LNK2019) and renders a reserved 320x50 slot between two Text siblings (PrintWindow capture).

No version bump / changelog per maintainer instruction.

Summary by CodeRabbit

  • New Features
    • Added a new Windows ad banner widget that reserves layout space using a lightweight placeholder.
    • Supports common banner size options with DPI-scaled fixed width/height for consistent UI layout.
    • Introduced a new UI-layer creation API to create and register the ad banner widget (returns a widget handle), including safe non-Windows behavior.

…rror

perry_ui_adbanner_create is a required dispatch row
(ui_table/part_a.rs AdBanner) but was never defined in the Windows
staticlib, so any TS program using AdBanner(...) died at link time
with an unresolved external. Implement the same layout-placeholder
semantics as the macOS backend (no Google Mobile Ads SDK exists for
either desktop OS): an empty STATIC pinned to the Google AdSize slot
dimensions (DPI-scaled) so cross-platform layouts reserve exactly the
space the live banner occupies on iOS/Android.

Verified: an AdBanner app now compiles, links, and renders a reserved
320x50 slot between siblings (previously: LNK2019).
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d54b1fd0-4c59-430d-8172-3e278849495d

📥 Commits

Reviewing files that changed from the base of the PR and between fa85ad1 and 87e2d3d.

📒 Files selected for processing (1)
  • crates/perry-ui-windows/src/widgets/mod.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/perry-ui-windows/src/widgets/mod.rs

📝 Walkthrough

Walkthrough

Adds a Windows ad banner placeholder widget that reserves a size-specific, DPI-scaled layout area, registers the widget, and exposes creation through a new C-compatible FFI function.

Changes

Windows ad banner

Layer / File(s) Summary
Ad banner widget implementation
crates/perry-ui-windows/src/widgets/mod.rs, crates/perry-ui-windows/src/widgets/ad_banner.rs
Registers the new widget module and implements banner size parsing, DPI-scaled dimensions, widget registration, and a Win32 STATIC placeholder window.
Windows FFI entrypoint
crates/perry-ui-windows/src/ffi/widget_create.rs
Adds perry_ui_adbanner_create, which converts runtime string pointers and forwards them to the ad banner factory.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Runtime
  participant FFI
  participant AdBanner
  participant WidgetRegistry
  participant Win32
  Runtime->>FFI: call perry_ui_adbanner_create
  FFI->>AdBanner: pass unit ID and size pointers
  AdBanner->>AdBanner: calculate banner dimensions
  AdBanner->>Win32: create STATIC placeholder
  AdBanner->>WidgetRegistry: register handle and fixed size
  WidgetRegistry-->>FFI: return widget handle
  FFI-->>Runtime: return i64 handle
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes adding the missing Windows AdBanner placeholder widget to resolve the unresolved external.
Description check ✅ Passed The description includes the problem, fix, and Windows verification, which satisfies the template's core requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/win-ui-adbanner

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/perry-ui-windows/src/widgets/ad_banner.rs`:
- Around line 25-35: Update str_from_header to use a bounded lifetime tied to
the input pointer instead of returning &'static str, and replace
from_utf8_unchecked with std::str::from_utf8. Return the existing empty-string
fallback when the byte slice is invalid while preserving the null-pointer
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 41b82b86-2a54-4cc1-b07a-5423b71758f0

📥 Commits

Reviewing files that changed from the base of the PR and between cca6e54 and 0dae82e.

📒 Files selected for processing (3)
  • crates/perry-ui-windows/src/ffi/widget_create.rs
  • crates/perry-ui-windows/src/widgets/ad_banner.rs
  • crates/perry-ui-windows/src/widgets/mod.rs

Comment on lines +25 to +35
fn str_from_header(ptr: *const u8) -> &'static str {
if ptr.is_null() {
return "";
}
unsafe {
let header = ptr as *const perry_runtime::string::StringHeader;
let len = (*header).byte_len as usize;
let data = ptr.add(std::mem::size_of::<perry_runtime::string::StringHeader>());
std::str::from_utf8_unchecked(std::slice::from_raw_parts(data, len))
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Prevent Undefined Behavior by safely parsing potentially invalid UTF-8 strings.

The perry_runtime::string::StringHeader explicitly documents that strings may contain lone surrogates (WTF-8). Calling std::str::from_utf8_unchecked on such strings invokes Undefined Behavior in Rust. Use std::str::from_utf8 and safely fall back (e.g., to an empty string) if the bytes are invalid.

Additionally, returning &'static str for a runtime-allocated string is semantically incorrect because the string's lifetime is bound to the runtime's memory, not the life of the program. It should use a bounded lifetime parameter.

🔒️ Proposed fix
-fn str_from_header(ptr: *const u8) -> &'static str {
+fn str_from_header<'a>(ptr: *const u8) -> &'a str {
     if ptr.is_null() {
         return "";
     }
     unsafe {
         let header = ptr as *const perry_runtime::string::StringHeader;
         let len = (*header).byte_len as usize;
         let data = ptr.add(std::mem::size_of::<perry_runtime::string::StringHeader>());
-        std::str::from_utf8_unchecked(std::slice::from_raw_parts(data, len))
+        std::str::from_utf8(std::slice::from_raw_parts(data, len)).unwrap_or("")
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-ui-windows/src/widgets/ad_banner.rs` around lines 25 - 35,
Update str_from_header to use a bounded lifetime tied to the input pointer
instead of returning &'static str, and replace from_utf8_unchecked with
std::str::from_utf8. Return the existing empty-string fallback when the byte
slice is invalid while preserving the null-pointer behavior.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Addressed in the follow-up commit: str_from_header now uses a checked std::str::from_utf8 with an empty-string fallback (invalid size key => default 320x50 slot), since runtime strings are WTF-8 and may carry lone surrogates. Kept the &'static signature for now - it matches the crate-wide str_from_header idiom used in ~15 sibling files (all of which also use from_utf8_unchecked and predate this PR); filing the crate-wide sweep separately so this PR stays scoped to the missing symbol.

Ralph Kuepper and others added 2 commits July 19, 2026 08:18
Runtime strings are WTF-8 (lone surrogates possible), so exposing the
raw bytes via from_utf8_unchecked is UB. Parse checked; invalid input
falls back to the empty string and therefore the default 320x50
banner slot. Addresses the CodeRabbit review on #6640. The same
unchecked idiom exists in ~15 sibling files predating this PR -
tracked separately.
@proggeramlug
proggeramlug merged commit ac3fa7a into main Jul 20, 2026
27 checks passed
@proggeramlug
proggeramlug deleted the fix/win-ui-adbanner branch July 20, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant