From f1a08fcf704f53457a59020df25952f79d3f47c3 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Thu, 29 Oct 2026 23:06:24 +0000 Subject: [PATCH] refactor(native): drop dead IsExceptionPending checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit binding.gyp defines NAPI_CPP_EXCEPTIONS, so RequireTerminal throws a C++ exception and no JavaScript exception can be pending at these checks. The README no longer mentions them. Signed-off-by: Thomas Kosiewski --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `medium`_ Change-Id: I57b99f0eb37eb11ab878d29a86ade1618be8804e --- native/README.md | 2 +- native/terminal.cc | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/native/README.md b/native/README.md index 5236fa9..2914c92 100644 --- a/native/README.md +++ b/native/README.md @@ -41,7 +41,7 @@ C++ declarations: - `cols` and `rows`: a non-number throws `TypeError(" must be a positive integer")`. `NaN`, an infinity, a value below 1 or above 65535, or a non-integer throws `RangeError(" must be a positive 16-bit integer")`. The range check runs before the value is cast to `uint16_t` [native/terminal.cc:PositiveUint16]. - `scrollbackLimit` is skipped when it is absent or `undefined`, and then defaults to 0. A non-number throws `TypeError`. `NaN`, an infinity, a negative value, a non-integer, or a value too large for `size_t` (2^64 or more on 64-bit targets) throws `RangeError`. Both use `"scrollbackLimit must be a non-negative integer"`, and the range check runs before the cast [native/terminal.cc:NonNegativeSize]. - Most failed libghostty-vt calls throw an `Error` with `ResultMessage` text, for example `"ghostty_terminal_new failed: GHOSTTY_OUT_OF_MEMORY"` [native/terminal.cc:ThrowResult]. These do not throw: a failed palette read leaves the color out, a failed `ghostty_build_info` leaves the field out, a first `ghostty_grid_ref_graphemes` call that does not return `GHOSTTY_OUT_OF_SPACE` with `required > 0` falls back to the single codepoint, and the code checks no result from `ghostty_terminal_vt_write` [native/terminal.cc:TerminalWrap::ResolveStyleColor] [native/terminal.cc:TerminalWrap::ReadCellText] [native/terminal.cc:TerminalWrap::Feed]. -- Every method except `dispose` and `encodeMouse` calls `RequireTerminal` first. After dispose it throws `Error("GhosttyVtTerminal has been disposed")` [native/terminal.cc:TerminalWrap::RequireTerminal]. With `NAPI_CPP_EXCEPTIONS`, `RequireTerminal` reports a disposed terminal by throwing, so the `env.IsExceptionPending()` checks after it do not see that error. +- Every method except `dispose` and `encodeMouse` calls `RequireTerminal` first. After dispose it throws `Error("GhosttyVtTerminal has been disposed")` [native/terminal.cc:TerminalWrap::RequireTerminal]. With `NAPI_CPP_EXCEPTIONS`, `RequireTerminal` reports a disposed terminal by throwing. - `encodeMouse` reads and checks every argument first, and only then calls `RequireTerminal` and `RequireMouseEncoder` (`"GhosttyVtTerminal has been disposed"`). `Get()` runs getters, also getters on `Object.prototype` for keys that the caller leaves out, and such JS can dispose the terminal or call `encodeMouse` again. After the handles are taken, no JS runs until the call returns [native/terminal.cc:TerminalWrap::EncodeMouse]. - `encodeMouse` throws `TypeError("encodeMouse expects event and options objects")` unless it gets exactly two objects, and `TypeError("mouse geometry must be an object")` for a non-object `geometry` [native/terminal.cc:TerminalWrap::EncodeMouse]. `screenWidth`, `screenHeight`, `cellWidth`, and `cellHeight` must be integers from 1 to 2^32-1, and the four paddings integers from 0 to 2^32-1: a non-number throws `TypeError(" must be a positive integer")` or `"... non-negative integer"`, and any other bad value `RangeError(" must be a positive 32-bit integer")` or `"... non-negative 32-bit integer"` [native/terminal.cc:PositiveUint32] [native/terminal.cc:NonNegativeUint32]. - `encodeMouse` geometry limits, the same calculation that Ghostty makes before its unchecked casts: the padding sum of an axis, added in 64 bits, must not exceed the screen size (`RangeError("mouse geometry padding exceeds screenWidth")`, or `screenHeight`), and `f32(screen - padding) / f32(cell)` must be below 65536 (`RangeError("mouse geometry.screenWidth exceeds the 65535-cell limit")`, or `screenHeight`) [native/terminal.cc:ValidateMouseAxis]. diff --git a/native/terminal.cc b/native/terminal.cc index fe1fcd9..d1b336e 100644 --- a/native/terminal.cc +++ b/native/terminal.cc @@ -280,7 +280,6 @@ GhosttyMouseEncoder TerminalWrap::RequireMouseEncoder(Napi::Env env) { Napi::Value TerminalWrap::Feed(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); GhosttyTerminal terminal = RequireTerminal(env); - if (env.IsExceptionPending()) return env.Undefined(); const uint8_t* data = nullptr; size_t len = 0; @@ -320,7 +319,6 @@ Napi::Value TerminalWrap::Feed(const Napi::CallbackInfo& info) { Napi::Value TerminalWrap::Resize(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); GhosttyTerminal terminal = RequireTerminal(env); - if (env.IsExceptionPending()) return env.Undefined(); if (info.Length() != 2) { throw Napi::TypeError::New(env, "resize expects cols and rows"); } @@ -462,7 +460,6 @@ Napi::Value TerminalWrap::EncodeMouse(const Napi::CallbackInfo& info) { Napi::Value TerminalWrap::Snapshot(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); RequireTerminal(env); - if (env.IsExceptionPending()) return env.Undefined(); bool include_scrollback = false; bool include_cells = false; @@ -485,7 +482,6 @@ Napi::Value TerminalWrap::Snapshot(const Napi::CallbackInfo& info) { Napi::Value TerminalWrap::GetVisibleText(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); RequireTerminal(env); - if (env.IsExceptionPending()) return env.Undefined(); try { uint16_t rows = 0; @@ -508,7 +504,6 @@ Napi::Value TerminalWrap::GetVisibleText(const Napi::CallbackInfo& info) { Napi::Value TerminalWrap::FormatPlain(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); RequireTerminal(env); - if (env.IsExceptionPending()) return env.Undefined(); try { return Napi::String::New(env, Format(GHOSTTY_FORMATTER_FORMAT_PLAIN, true)); } catch (const std::exception& err) { @@ -519,7 +514,6 @@ Napi::Value TerminalWrap::FormatPlain(const Napi::CallbackInfo& info) { Napi::Value TerminalWrap::FormatHtml(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); RequireTerminal(env); - if (env.IsExceptionPending()) return env.Undefined(); try { return Napi::String::New(env, Format(GHOSTTY_FORMATTER_FORMAT_HTML, true)); } catch (const std::exception& err) {