Detect variadic arities of :error interceptors - #795
The-Alchemist wants to merge 1 commit into
Conversation
|
I had a look at this but I'm unfortunately not familiar with pedestal stuff so I couldn't approve (yet). |
5e7c2d2 to
e73594e
Compare
|
@opqdonut : here's some LLM analysis, if it helps at all: PR #795 ( What Pedestal is, in one paragraphPedestal is a Clojure HTTP framework. Instead of Ring-style middleware wrapping a handler, it uses interceptors: named steps with optional Reitit can replace Pedestal’s router. Reitit’s own interceptors (exceptions, coercion, etc.) come from a different interceptor library, Sieppari. The two models almost match. The mismatch that matters here is error handlers:
When Reitit turns a Reitit interceptor into a Pedestal interceptor, it looks at the The bugThat “does it take 2 arguments?” check used Java reflection on the function’s The code then assumed “not 2-arity” and always wrapped those handlers. A handler that was actually Pedestal-style (2 args, possibly via A second issue: some Clojure runtimes (including a GraalVM-oriented fork) do not give each function its own class with only the real arities. Reflection there can make every function look like it accepts every arity, so the wrap/don’t-wrap decision is also wrong. What the PR changesOnly
Fixed-arity handlers behave as before. Variadic Who caresIf you write Pedestal |
Arity detection reflected over the `invoke` methods declared on a fn's class, which cannot see variadic arities: those are compiled to `doInvoke`, so a variadic `:error` handler reported no arities at all and was always treated as 1-arity and wrapped by `error-arity-2->1`. This was the existing `;; TODO: variadic`. `signatures` now prefers `:arglists` metadata when present, and otherwise consults `RestFn.getRequiredArity` alongside the declared `invoke` methods, so both purely variadic and mixed fixed/variadic fns are handled. `:arglists` is checked first because it is the portable description of a fn's signatures. Class reflection assumes the JVM compiler's one class per fn with only its declared arities, which does not hold on runtimes that represent every fn with a single shared class; those declare every `invoke` overload, making every handler look like it accepts any arity. Signatures record whether they are variadic rather than collapsing to a set of arities, since a set cannot express "n arguments or more" without inventing an upper bound. The one call site, `error-without-arity-2?`, asks `accepts-arity?` instead of testing set membership. Signed-off-by: The-Alchemist <kap4020@gmail.com>
e73594e to
6a37ec6
Compare
Summary
Variadic arities are compiled to
doInvoke, notinvoke, so they areinvisible here:
faritiesbefore(fn [& _])#{}(fn [_ & _])#{}(fn ([_]) ([_ _ & _]))#{1}The one caller is
error-without-arity-2?, so a variadic:errorhandler is misread as not supporting 2-arity and gets wrapped by
error-arity-2->1. A handler written as(fn [context ex] ...)via& args, or one returned by a combinator such aspartial/comp,is silently called with the Sieppari 1-arity convention instead of
Pedestal's 2-arity one.
Change
aritiesnow prefers:arglistsmetadata when present, and otherwisecombines the declared
invokemethods withRestFn.getRequiredArity,which covers both purely variadic and mixed fixed/variadic fns.
:arglistsis checked first because it is the portable description of afn's signatures. Class reflection encodes an assumption specific to the
JVM compiler — one class per fn, declaring only the arities it defines.
That does not hold on alternative Clojure runtimes which represent every
fn with one shared class implementing all
invokeoverloads; there,aritiesreturns#{0..21}and every:errorhandler looks like itaccepts any arity. Honouring
:arglistsgives such runtimes (and anyoneusing
with-meta) a portable way to report real arities, at no cost onthe JVM where anonymous fns have no metadata and the reflection path
is unchanged.
The single call site now uses a small
accepts-arity?helper instead ofcontains?on a set, because a set of arities cannot express "narguments or more" without inventing an upper bound.
Apologies for the LLM summary!
Motivation
The original motivation is that we are working on a GraalVM fork of Clojure at https://github.com/The-Alchemist/cloffle-clojure which does not generate JVM bytecode so
.getDeclaredFieldsdoesn't exist. Adding variadic support does two things at once.Compatibility
No public API change;
aritiesis private. Behaviour for non-variadicfns is unchanged, including the existing
arities-testassertions.Variadic
:errorhandlers that accept 2 args are no longer wrapped —that is the bug fix, and it only affects handlers that were being
called with the wrong convention before.
Opened as a draft — happy to add a CHANGELOG entry or adjust the shape
of the helpers if you would prefer to keep this to a single fn.