Repository: tauri-apps/wry (language: rust)

Issue:
macOS: `WebView::evaluate_script` crashes only on release build
**Describe the bug**

Calling `WebView::evaluate_script` on release build causes SEGV on macOS.

```
$ cargo run --example test --release --features=devtools
   Compiling wry v0.35.2 (/Users/rhysd/Develop/github.com/tauri-apps/wry)
    Finished release [optimized] target(s) in 2.69s
     Running `target/release/examples/test`
[1]    50589 segmentation fault  cargo run --example test --release --features=devtools
```

I investigated where the crash happend and located the place:

https://github.com/tauri-apps/wry/blob/52c861bbef00f2762ae837e01b7d36fcb13cd0bc/src/wkwebview/mod.rs#L986

The `completionHanlder` is the cause of the crash. When I removed the handler as follows, the crash was fixed:

```rust
msg_send![self.webview, evaluateJavaScript:NSString::new(js) completionHandler:null::<*const c_void>()]
```

The `completionHandler` was introduced at #1133 so this bug was introduced at v0.35.2 and it is not in v0.35.1. This bug is not reproducible on debug build.

Here is a stacktrace obtained with rust-lldb:

```
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
  * frame #0: 0x00007ff81145021d libobjc.A.dylib`objc_msgSend + 29
    frame #1: 0x00007ff91466136c WebKit`-[WKWebView _evaluateJavaScript:asAsyncFunction:withSourceURL:withArguments:forceUserGesture:inFrame:inWorld:completionHandler:] + 117
    frame #2: 0x00007ff9146608fa WebKit`-[WKWebView evaluateJavaScript:completionHandler:] + 100
    frame #3: 0x0000000100022b3b test`objc_exception::try_no_ret::try_objc_execute_closure::hb1268a266285b8b3 (.llvm.13774043197985141995) + 107
    frame #4: 0x0000000100086e02 test`RustObjCExceptionTryCatch + 18
    frame #5: 0x0000000100022dc2 test`objc_exception::try::h22ee83a425700ec7 + 114
    frame #6: 0x0000000100020bec test`objc::message::platform::send_unverified::h982c5b71120c5fea + 108
    frame #7: 0x000000010001b1a1 test`wry::wkwebview::InnerWebView::eval::h31c6ac913dff49ed + 1105
    frame #8: 0x0000000100028fd1 test`wry::WebView::evaluate_script::h27856c5c7484d11d + 17
    frame #9: 0x0000000100008812 test`test::main::_$u7b$$u7b$closure$u7d$$u7d$::h69ce4f1b0075d085 + 82
    frame #10: 0x000000010000bc34 test`_$LT$tao..platform_impl..platform..app_state..EventLoopHandler$LT$T$GT$$u20$as$u20$tao..platform_impl..platform..app_state..EventHandler$GT$::handle_user_events::hade95c0e949521f0 + 292
    frame #11: 0x000000010004ed98 test`tao::platform_impl::platform::app_state::AppState::cleared::h82e4f94ab3d2ae4f + 840
    frame #12: 0x0000000100055460 test`std::panicking::try::ha1f81f819c3b13eb + 64
    frame #13: 0x0000000100050359 test`tao::platform_impl::platform::observer::control_flow_end_handler::h9474723844f78e94 (.llvm.15874453372557250967) + 105
    frame #14: 0x00007ff8118c03d4 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    frame #15: 0x00007ff8118c02fb CoreFoundation`__CFRunLoopDoObservers + 482
    frame #16: 0x00007ff8118bf9a7 CoreFoundation`__CFRunLoopRun + 1148
    frame #17: 0x00007ff8118beec1 CoreFoundation`CFRunLoopRunSpecific + 560
    frame #18: 0x00007ff81b340f3d HIToolbox`RunCurrentEventLoopInMode + 292
    frame #19: 0x00007ff81b340d4e HIToolbox`ReceiveNextEventCommon + 657
    frame #20: 0x00007ff81b340aa8 HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 64
    frame #21: 0x00007ff81495b25c AppKit`_DPSNextEvent + 858
    frame #22: 0x00007ff81495a106 AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1214
    frame #23: 0x00007ff81494c788 AppKit`-[NSApplication run] + 586
    frame #24: 0x000000010000cb09 test`objc_exception::try_no_ret::try_objc_execute_closure::haf58d3cce505f1d0 (.llvm.14017015814591196138) + 41
    frame #25: 0x0000000100086e02 test`RustObjCExceptionTryCatch + 18
    frame #26: 0x000000010000b91d test`objc::message::platform::send_unverified::hfa2b948746ba6d32 + 109
    frame #27: 0x0000000100007ce4 test`tao::platform_impl::platform::event_loop::EventLoop$LT$T$GT$::run::hbdf9c6ded7f3ed2a + 500
    frame #28: 0x000000010000a90d test`test::main::hbf3cc6f3d30af690 + 2077
    frame #29: 0x000000010000e34b test`std::sys_common::backtrace::__rust_begin_short_backtrace::ha8e9dd4a3810ab53 + 11
    frame #30: 0x000000010000d10b test`std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::he6f9525576c073ef + 27
    frame #31: 0x00000001000a1456 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] core::ops::function::impls::_$LT$impl$u20$core..ops..function..FnOnce$LT$A$GT$$u20$for$u20$$RF$F$GT$::call_once::h653df530b3183ae2 at function.rs:284:13 [opt]
    frame #32: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] std::panicking::try::do_call::h6acb6e379e3af458 at panicking.rs:552:40 [opt]
    frame #33: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] std::panicking::try::h9fbfaabccd4dd967 at panicking.rs:516:19 [opt]
    frame #34: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] std::panic::catch_unwind::h118c5010bfea0770 at panic.rs:142:14 [opt]
    frame #35: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::h2d12577519fab1a9 at rt.rs:148:48 [opt]
    frame #36: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] std::panicking::try::do_call::h3ed9f86b3d1d1360 at panicking.rs:552:40 [opt]
    frame #37: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] std::panicking::try::h1d5ad7b282666b47 at panicking.rs:516:19 [opt]
    frame #38: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a [inlined] std::panic::catch_unwind::ha8a4032844388a31 at panic.rs:142:14 [opt]
    frame #39: 0x00000001000a1449 test`std::rt::lang_start_internal::h13f88294184c535a at rt.rs:148:20 [opt]
    frame #40: 0x000000010000d0e5 test`std::rt::lang_start::hb4e24a739cf1a9fc + 37
    frame #41: 0x00007ff81148b41f dyld`start + 1903
```

**Steps To Reproduce**

Save the following file as `examples/test.rs`.

```rust
use tao::{
  event::{Event, WindowEvent},
  event_loop::{ControlFlow, EventLoop},
  window::WindowBuilder,
};
use wry::WebViewBuilder;

fn main() -> wry::Result<()> {
  let event_loop = EventLoop::new();
  let window = WindowBuilder::new().build(&event_loop).unwrap();

  let builder = WebViewBuilder::new(&window);
  let html = "
  <html>
    <body><button onclick=\"send()\">Send</button></body>
    <script>
      function send() {
        window.ipc.postMessage('eval');
      }
    </script>
  </html>
  ";
  let proxy = event_loop.create_proxy();
  let webview = builder
    .with_html(html)?
    .with_devtools(true)
    .with_ipc_handler(move |_| {
      proxy.send_event(()).unwrap();
    })
    .build()?;
  webview.open_devtools();

  event_loop.run(move |event, _, control_flow| {
    *control_flow = ControlFlow::Wait;

    match event {
      Event::WindowEvent {
        event: WindowEvent::CloseRequested,
        ..
      } => *control_flow = ControlFlow::Exit,
      Event::UserEvent(_) => {
        webview
          .evaluate_script("console.log('from evaluate_script')")
          .unwrap();
      }
      _ => {}
    }
  });
}
```

Then run the example with the following command:

```rust
cargo run --example test --release --features=devtools
```

WebView window shows up. Find 'Send' button in the window and press it.

**Expected behavior**

'from evaluate_script' is output in DevTools console.

**Screenshots**

N/A

**Platform and Versions (please complete the following information):**
OS: macOS 13.6.1 (Intel Mac)
Rustc: 1.75.0
wry: 33609e95ebf4eaef0be470b9a1de0f3b7a005ab5

**Additional context**

Nothing.




Discussion / hints:
I somehow found the root cause and could fix this issue. I'll make a pull request soon.

Fix patch:
diff --git a/.changes/fix-eval-crash.md b/.changes/fix-eval-crash.md
new file mode 100644
index 000000000..55eda3a2d
--- /dev/null
+++ b/.changes/fix-eval-crash.md
@@ -0,0 +1,5 @@
+---
+"wry": patch
+---
+
+On macOS, fix a release build crashes with SEGV when calling `WebView::evaluate_script`. This crash bug was introduced at v0.35.2.
diff --git a/src/wkwebview/mod.rs b/src/wkwebview/mod.rs
index 9be5703cd..fe7c3c901 100644
--- a/src/wkwebview/mod.rs
+++ b/src/wkwebview/mod.rs
@@ -956,7 +956,7 @@ r#"Object.defineProperty(window, 'ipc', {
         let span = Mutex::new(Some(tracing::debug_span!("wry::eval").entered()));
 
         // we need to check if the callback exists outside the handler otherwise it's a segfault
-        if let Some(callback) = &callback {
+        if let Some(callback) = callback {
           let handler = block::ConcreteBlock::new(move |val: id, _err: id| {
             #[cfg(feature = "tracing")]
             span.lock().unwrap().take();
@@ -972,15 +972,18 @@ r#"Object.defineProperty(window, 'ipc', {
             }
 
             callback(result);
-          });
+          }).copy();
 
           let _: () =
             msg_send![self.webview, evaluateJavaScript:NSString::new(js) completionHandler:handler];
         } else {
+          #[cfg(feature = "tracing")]
           let handler = block::ConcreteBlock::new(move |_val: id, _err: id| {
-            #[cfg(feature = "tracing")]
             span.lock().unwrap().take();
-          });
+          })
+          .copy();
+          #[cfg(not(feature = "tracing"))]
+          let handler = null::<*const c_void>();
 
           let _: () =
             msg_send![self.webview, evaluateJavaScript:NSString::new(js) completionHandler:handler];
@@ -1020,7 +1023,7 @@ r#"Object.defineProperty(window, 'ipc', {
       let store: id = msg_send![config, websiteDataStore];
       let all_data_types: id = msg_send![class!(WKWebsiteDataStore), allWebsiteDataTypes];
       let date: id = msg_send![class!(NSDate), dateWithTimeIntervalSince1970: 0.0];
-      let handler = block::ConcreteBlock::new(|| {});
+      let handler = null::<*const c_void>();
       let _: () = msg_send![store, removeDataOfTypes:all_data_types modifiedSince:date completionHandler:handler];
     }
     Ok(())


Test patch: