@borkdude I got SCI compiling (tests fail) in ClojureCLR. All other platforms are green. https://github.com/babashka/sci/pull/1006 Might this be a good place to merge to master?
Feel free to resume this whenever you'd like. I think the PR looks good now from my side.
Approved, added a couple of doc suggestions and license preservation from Reflection.java.
do you agree with the widen-box-args! mutation of the array (instead of re-assigning args)?
I asked Alex if re-assigning the args was essential. He said it was, but I couldn't find a single case where it mattered. But as a compromise I made widen-box-args mutate the incoming array which isn't shared with anything else
Yep that's an elegant solution imo.
nice, merged. so hopefully the CLR work will be easier now for SCI
@ambrosebs Amazing you got this far. But with failing tests, why would it be good to merge with master? I'd like to keep CI green on master?
If you have any questions I can help you progress further of course
My main motivation is future dread for complex merges. I can keep plugging away. The interesting questions are next: how to replace the custom reflector.
I think the lowest hanging fruit is fix the getId warning that shows a 1000x-ish times in CI now. This is the id of the current thread
aaah yes. the reflector
can we compile the reflector and publish it on git?
currently prep steps are not supported by cljr
so I guess committing it woudl be the only option atm
it's written in Java
Yes, sorry I meant porting it to C#, then dealing with the publishing issue
if we just commit the .dll or whatever .net uses we could do that I guess
fair. could you give me some context about what it does vs the built in reflector. I haven't looked at it yet
or we could port it to clojure
I think I've annotated the difference with the original
differences
it's basically a copy + some differences
nice!
/** clojure.lang.Reflector adapted for sci **/
/** **/
/** Patches made:
- Extra imports after package decl.
- Made invokeMatchingMethod public (around line 169)
- Compiler.FISupport was extracted into sci.impl.FISupport
**/ /** PATCH **/
import clojure.lang.Util;
import clojure.lang.RT;
import clojure.lang.Compiler;
import clojure.lang.IFn;
import java.lang.reflect.Proxy;
/** END PATCH **//* PATCH: made public */
public static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args){
return invokeMatchingMethod(methodName, methods, target != null ? target.getClass() : null, target, args);
}/* PATCH: made public, added argTypes */
public static Object invokeMatchingMethod(String methodName, List methods, Class contextClass, Object target, Object[] args, Class[] argTypes)
etcoh this is much less intimidating than I expected 🙂
if you want to port it to clojure, that's fine, but I will only be happy if there's not a severe drop in performance :)
yeah just porting it to .cljr might be a good first step, or asking clojure-clr to make these methods public
it's not just make them public but also "added argTypes" for example
it's an extra argument
clojure.lang.Reflector doesn't do anything with type hints, but my custom reflector does respect them
oh neat
yeah this should really live in the clojure-clr reflector. I'll propose it upstream
does .net not have something like mvn?
I don't think that clr will accept it since it's not a reflection of JVM Clojure? it has changes
there's something called NuGet but my eyes glaze over.
I don't think that clr will accept it since it's not a reflection of JVM Clojure? it has changesyeah it's possible it won't be accepted
> -- We support git lib and local directory dependencies only. We do not support maven dependencie or local jars. (Though we should have a discussion about the latter.) -- We are still thinking about how nuget might come into play. It's complicated. -- We do not yet have support for tool publishing and preparation steps. See below. https://github.com/clojure/clr.core.cli?tab=readme-ov-file#depsedn-features
on second thought, you're probably right, I'll just write a .cljr file and skip over the other complexities like cljr limitations and clj perf.
I'd be happy to have it .cljc if performance is good
that would also make porting SCI to ClojureDart easier
ooh nice.
there might also be a bunch of unused stuff in the reflector
so just begin from sci.impl.reflector see what is called
not sure if everything is expressible in clojure
I could also go the other direction and see if hello world even works in cljr. But this seems more fun 🙂
both seem fun
agreed and exciting
there's also a native-image type of thing for dotnet:
dotnet publish -r linux-x64 -c Release /p:PublishAot=true
so one could possibly make a fast starting bb-like thing for dotnet as wellnot sure how well it works/is adopted
pfff:
/usr/local/share/dotnet/sdk/9.0.306/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets(107,5): error NETSDK1204: Ahead-of-time compilation is not supported on the current platform 'osx-arm64'.boo
I decided to dispatch copilot in both directions. apparently the bottom-up direction is promising:
> SCI has excellent support for non-interop scenarios on ClojureCLR! ✅
> All tested features work perfectly:
> • ✅ Basic data types and collections
> • ✅ Anonymous functions and function literals
> • ✅ Control flow (if, when, and, or)
> • ✅ Sequence operations (map, filter, reduce)
> • ✅ Threading macros (→, →→)
> • ✅ Variable definition and namespaces
> • ✅ Higher-order functions (apply, comp, partial)
> • ✅ Evaluation with context/bindings
> Known limitations (require custom reflector not yet ported):
> • ❌ defn - function definitions
> • ❌ loop/recur
> • ❌ Destructuring
>
why are those last three dependent on reflector? should not be
agreed, I'm assuming there's some unrelated porting error
I'll look into that manually
I talked about the reflector being the major missing piece in the prompt, probably latched onto that as the reason for all problems.
lol, yeah the easy way out
to its credit it fixed the getId problem without me even asking
tho I don't really know what that is yet, didn't look into it myself
it is just an identifier for the current thread
ah, yeah simple fix
I wonder if we do this translation in one commit, if it can be used as training data for the next platform.
copilot really struggles with parens matching with reader conditionals tho.
it's ok with indentation. I had an idea to direct it to use par-infer to infer to parens based on indentation, instead of what it usually does, which is write python programs to count parens.
also for it to use edamame for code refactoring.
have you ever tried manipulating/querying programs with edamame? e.g., find all reader conditionals that lack a default branch and move the :clj branch as the final branch and rename to :default.
seems more like a job for rewrite-clj
are you just using copilot or also clojure-mcp or so?
I'm just using copilot agents with a .github/workflows/copilot-setup-steps.yml tailored for clojure
e.g., https://github.com/typedclojure/typedclojure/blob/main/.github/workflows/copilot-setup-steps.yml
first iteration of the cljc reflector is promising, only native tests fail https://github.com/frenchy64/sci/pull/2
so far I've really disliked having AI in my editor, but had great success just iterating in PR's.
if native tests fail then it's reflection likely
there's errors like Caused by: java.lang.IllegalArgumentException: No matching field found: getParameterTypes for class java.lang.reflect.Method
in fact I think that's the only error
do I add this to reflection.json?
no
let me look at the code
you don't get a reflection warning with warn on reflection?
did you require it in clojure to check?
I'm just looking at the huge output from the native CI tests
haven't tried it locally yet
run it locally and fix the reflection warnings
reflection in the reflection will make it way slower for sure
aha
Reflection warning, sci/impl/reflector.cljc:129:55 - reference to field getClass can't be resolved.
Reflection warning, sci/impl/reflector.cljc:134:45 - reference to field getClass can't be resolved.
Reflection warning, sci/impl/reflector.cljc:271:44 - reference to field getClass can't be resolved.
Reflection warning, sci/impl/reflector.cljc:317:68 - reference to field getParameterTypes can't be resolved.
Reflection warning, sci/impl/reflector.cljc:317:29 - call to method invoke can't be resolved (target class is unknown).
Reflection warning, sci/impl/reflector.cljc:318:40 - reference to field getReturnType can't be resolved.$ clj
Clojure 1.12.1
user=> (load-file "/tmp/reflector.cljc")
Reflection warning, /tmp/reflector.cljc:129:55 - reference to field getClass can't be resolved.
Reflection warning, /tmp/reflector.cljc:134:45 - reference to field getClass can't be resolved.
Reflection warning, /tmp/reflector.cljc:271:44 - reference to field getClass can't be resolved.
Reflection warning, /tmp/reflector.cljc:317:68 - reference to field getParameterTypes can't be resolved.
Reflection warning, /tmp/reflector.cljc:317:29 - call to method invoke can't be resolved (target class is unknown).
Reflection warning, /tmp/reflector.cljc:318:40 - reference to field getReturnType can't be resolved.
#'sci.impl.reflector/invoke-matching-methodyeah
why oh why is .getClass a reflection warning.
just slap an ^Object tag on it
but why not hardcode object methods? I don't understand
philosophically
what do you mean with hardcode object methods?
is .getClass ever ambiguous? could it refer to anything but Object/.getClass? this is what I'm unsure about. if not, couldn't we (clojure) elide the need for ^Object.
don't know, I just know that I've needed to to this to avoid reflection 🤷
ok we're on the same page 😄
I guess reflector doesn't have a special case for Object methods
maybe you could have a field called .getClass?
yeah could also be it
who wins in that case? (.getClass foo) if foo has a field and method with 0 params
haha maybe the answer is literally in the code I'm refactoring.
I think getClass field wins
ah. ok then maybe a similar idea with (Object/.equals foo bar)
yes
I wonder why (.getClass ^Number foo) is a reflection warning.
a subclass could have a field I guess?
user=> ((fn [x] (.getClass ^Number x)) 1)
java.lang.Longoh wait it's not a reflection warning. I swear I just fixed that in the code.
oh reflection warning I see
user=> (set! *warn-on-reflection* true)
true
user=> ((fn [x] (.getClass ^Number x)) 1)
java.lang.Longnvm it works 🙂
you might also want to enable:
(set! *unchecked-math* :warn-on-boxed)probably not as important as reflection warnings but might prevent some unnecessary boxing here and there
oh interesting!
user=> (deftype Foo [getClass])
user.Foo
user=> (#(.getClass ^Foo %) (->Foo 1))
user.Foo
yeah I guess we can save these edge cases for some other time :)
haha yes xD
although it might be worth it to file it in clojure-dev
yes, thanks for talking me through that. back to the task.
all green with the resolved reflection. should I pr? https://github.com/frenchy64/sci/pull/2
yes. I won't merge it immediately, but will push it to a branch first so I can also test it against babashka and do some local reflection performance tests.
and bb CI
to see if it passes all tests
this is again exciting!
great. I will just copy it to a .cljr and customize it there. I think this will solve the clr reflection issue.
good, but you do make a PR to SCI right?
no I made a PR to my own fork, IIRC the tests weren't running
I mean will
yes squashing and pr'ing.
🎉
maybe this could eventually be a public library even
although the market for it would be very niche probably ;)
(I haven't needed something like this in other projects yet)
😄 sorta like backtick, you never need it until you do https://github.com/brandonbloom/backtick
yeah until now I've never really needed that one
although I can see how I could use it in squint maybe
to make gensyms in macro bodies more deterministic
right now I'm threading my own gensym through macro envs
anyway, this is also a tangent :)
I used it to iterate more quickly on exploring optimizations to Clojure's own syntax-quote 🙂
I put a big reminder we need to comb through each line before merging 🙂 https://github.com/babashka/sci/pull/1007
lol, look how I mistyped locally:
$ checkout -b frenchy64-cljc-reflector master
git pull git@github.com:frenchy64/sci.git cljc-reflector
zsh: command not found: checkout
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 11 (delta 8), reused 11 (delta 8), pack-reused 0 (from 0)
Unpacking objects: 100% (11/11), 4.37 KiB | 372.00 KiB/s, done.
From
* branch cljc-reflector -> FETCH_HEAD
Updating f40ee1a..cdf978d
Fast-forward
deps.edn | 4 +-
project.clj | 1 -
reflector/.gitignore | 1 -
reflector/project.clj | 10 ---
reflector/script/deploy | 3 -
reflector/src/sci/impl/FISupport.java | 32 -------
reflector/src/sci/impl/Reflector.java | 749 --------------------------------------------------------------------------------------------------------------------------------------------------------------
src/sci/impl/analyzer.cljc | 15 ++--
src/sci/impl/interop.cljc | 18 ++--
src/sci/impl/reflector.cljc | 326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sci/impl/test.cljc | 1 -
11 files changed, 343 insertions(+), 817 deletions(-)
delete mode 100644 reflector/.gitignore
delete mode 100644 reflector/project.clj
delete mode 100755 reflector/script/deploy
delete mode 100644 reflector/src/sci/impl/FISupport.java
delete mode 100644 reflector/src/sci/impl/Reflector.java
create mode 100644 src/sci/impl/reflector.cljc or miscopied
now it's locally on my master branch 😂
LOL
ok, there it goes on bb CI. 106 clojure libs are tested there. if it passes, I think it should be good, but then should screen the code for some perf issues maybe. https://github.com/babashka/babashka/tree/frenchy64-cljc-reflector
the linux job on circleCI: https://app.circleci.com/pipelines/github/babashka/babashka/7848/workflows/fc8959aa-ce00-49e5-8b3c-e5e554d0e6a0/jobs/43488
would also be interesting to see the effect on image size but I hope it's not so much
all interop (unit) tests pass locally
lol, looks like interop got even faster:
borkdude@MBP25-2 ~/dev/babashka (frenchy64-cljc-reflector) $ /opt/homebrew/bin/bb -e '(time (dotimes [i 1000000] (.toString 1)))'
"Elapsed time: 2089.690417 msecs"
borkdude@MBP25-2 ~/dev/babashka (frenchy64-cljc-reflector) $ /opt/homebrew/bin/bb --version
babashka v1.12.208
borkdude@MBP25-2 ~/dev/babashka (frenchy64-cljc-reflector) $ ./bb -e '(time (dotimes [i 1000000] (.toString 1)))'
"Elapsed time: 1291.895917 msecs"hmm, no it's probably just a matter of which machine it was compiled on or so.
$ tmp/bb-interop -e '(time (dotimes [i 1000000] (.toString 1)))'
"Elapsed time: 476.7175 msecs"I'll compile the master branch locally for comparison
From CI:
ERROR in (websockets-test) (test_utils.clj:100)
expected: (= "zomg websockets!" (bb (quote (do (ns net (:require [clojure.string :as str]) (:import ( URI) (java.net.http HttpClient WebSocket$Listener) (java.util.concurrent CompletableFuture) (java.util.function Function))) (let [p (promise) uri (URI. "") listener (reify WebSocket$Listener (onOpen [_ ws] (.request ws 1)) (onText [_ ws data last?] (.request ws 1) (.thenApply (CompletableFuture/completedFuture nil) (reify Function (apply [_ _] (deliver p (str data))))))) client (HttpClient/newHttpClient) ws (-> (.newWebSocketBuilder client) (.buildAsync uri listener) (deref))] (.sendText ws "zomg websockets!" true) (deref p 5000 :babashka.java-net-http-test/timeout))))))
actual: clojure.lang.ExceptionInfo: ----- Error --------------------------------------------------------------------
Type: java.lang.NoSuchMethodException
Message: clojure.lang.Reflector.getAsMethodOfAccessibleBase(java.lang.Class, java.lang.reflect.Method, java.lang.Object)
Location: NO_SOURCE_PATH:1:559 some more:
ERROR in (send-test) (test_utils.clj:100)
expected: (= [200 true] (bb (quote (do (ns net (:import ( URI) (java.net.http HttpClient HttpRequest HttpResponse$BodyHandlers))) (def req (-> (HttpRequest/newBuilder (URI. "")) (.GET) (.build))) (def client (HttpClient/newHttpClient)) (def res (.send client req (HttpResponse$BodyHandlers/ofString))) [(.statusCode res) (string? (.body res))]))))
actual: clojure.lang.ExceptionInfo: ----- Error --------------------------------------------------------------------
Type: java.lang.NoSuchMethodException
Message: clojure.lang.Reflector.getAsMethodOfAccessibleBase(java.lang.Class, java.lang.reflect.Method, java.lang.Object) one that is a little bit more narrowed down:
ERROR in (java-time-test) (test_utils.clj:100)
expected: (= "GMT+03:00" (bb "(System/setProperty \"user.timezone\" \"GMT+3\") (.getId (java.time.ZoneId/systemDefault))"))
actual: clojure.lang.ExceptionInfo: ----- Error --------------------------------------------------------------------
Type: java.lang.NoSuchMethodException
Message: clojure.lang.Reflector.getAsMethodOfAccessibleBase(java.lang.Class, java.lang.reflect.Method, java.lang.Object)$ ./bb -e "(.getId (java.time.ZoneId/systemDefault))"
----- Error --------------------------------------------------------------------
Type: java.lang.NoSuchMethodException
Message: clojure.lang.Reflector.getAsMethodOfAccessibleBase(java.lang.Class, java.lang.reflect.Method, java.lang.Object)
Location: NO_SOURCE_PATH:1:1this one does work:
$ ./bb -e "(java.time.ZoneId/systemDefault)"
#object[java.time.ZoneRegion 0x40bce22 "Europe/Amsterdam"]Ah can you use ZoneId/.getId ?
yes:
$ ./bb -e "(java.time.ZoneId/.getId (java.time.ZoneId/systemDefault))"
"Europe/Amsterdam"guessing it didn't faithfully port the argTypes stuff
there's actually no arg type here, arg type only works with explicit type hints, there's no return type inference in interop in SCI (yet)
oh! yeah I'm a bit lost in all the layers
this is reflection happening at runtime during sci's interpreter?
yes
so it should just know it's a ZoneId since it has the value of the target?
but now that we have it in clojure it's a lot easier to debug. let me have a look...
brilliant
drat, the in the JVM it does work :)
$ clj -M:babashka/dev -e "(.getId (java.time.ZoneId/systemDefault))"
"Europe/Amsterdam"
so I'll have to compile bb to debug, but that's okhaha disappointed that it works
wtf is this?
(let [reflector-class clojure.lang.Reflector
method (.getMethod reflector-class "getAsMethodOfAccessibleBase"
(into-array Class [Class Method Object]))]
(.invoke method nil
(object-array [(or context-class (.getDeclaringClass m))
m
target])))I don't know
I mean, why does it call clojure.lang.Reflector in such a cumbersome way
ooh I know
because clojure.lang.Reflector has this as a dynamic method which is conditioned on the JVM version
I think I accidentally included that, it was originally this:
(Reflector/getAsMethodOfAccessibleBase
(or context-class (.getDeclaringClass m))
m
target)
I asked copilot to fix the native errors and that's what it came up with. then I reverted it but obviously not correctly.oh no wait, other way around.
it was originally the "wtf" code, and it attempted to rewrite to the above, but it broke everything
so I reverted it back to wtf
oh yes, it's just a public method:
public static Method getAsMethodOfAccessibleBase(Class c, Method m, Object target){
for(Class iface : c.getInterfaces())I think this is a reflection issue
native-image doesn't have this method in its config so it can't call it, so that explains it
just normally calling it should fix it
this was the kind of error for the non-wtf version (jvm):
Error: Exception in thread "main" java.lang.IllegalArgumentException: No matching method: getAsMethodOfAccessibleBase, compiling:(sci/impl/reflector.cljc:305:36)https://github.com/frenchy64/sci/actions/runs/18981541041/job/54215078220
just this should work:
(clojure.lang.Reflector/getAsMethodOfAccessibleBase (or context-class (.getDeclaringClass m))
m
target)
let me test locallyyep:
$ ./bb -e "(.getId (java.time.ZoneId/systemDefault))"
"Europe/Amsterdam"pushed it to SCI branch
if you have additions, let's PR to that branch
Ran 255 tests containing 666 assertions.
0 failures, 0 errors.WOW
hmm lsp says there are 0 references to this function which is a bit weird
oh I have lsp set in the babashka root
whatever
even more simple, since we can assume JVM 9+
(or (not (Modifier/isPublic
(.getModifiers (.getDeclaringClass m))))
(and target
(.canAccess m target)))or maybe not since clojure still supports 8. we can just say we don't anymore with SCI
https://github.com/babashka/sci/commit/2a9316fd4bc3099450ea34fa3b71579bdee04a94
well this is looking good:
$ ./bb -e "(time (dotimes [i 1000000] (.getId (java.time.ZoneId/systemDefault))))"
"Elapsed time: 2168.60525 msecs"
$ tmp/bb-without-cljc-reflector2 -e "(time (dotimes [i 1000000] (.getId (java.time.ZoneId/systemDefault))))"
"Elapsed time: 3312.227791 msecs"huh??
{:test 5508, :pass 20431, :fail 0, :error 0}pretty drastic perf difference for supposedly a transliteration
maybe it's just because we removed the dynamic invocation of the method. not sure. graalvm is a bit of a blackbox to me
anyway I think it's good to go actually
ha! ok but I never actually read the code closely. I'll take a few minutes later today to step through each line to ensure it's transliterated faithfully.
hanks
it generated weird stuff like == to = instead of identical?. I fixed those up, but maybe more.
thanks
I actually introduced a few == myself
into the clojure ocde
for numeric comparison
how does the jvm handle that for numerics?
is it really pointer identity?
I guess Clojure compiles that to (= 1 2) in bytecode
or something similar, don't know
maybe identical? is even better if you know the types are the same
oh:
user=> (require '[clj-java-decompiler.core :as d])
nil
user=> (d/decompile (fn [x y] (== x y)))
// Decompiling class: user$fn_line_1__254
import clojure.lang.*;
public final class user$fn_line_1__254 extends AFunction
{
public static Object invokeStatic(final Object x, final Object y) {
return Numbers.equiv(x, y) ? Boolean.TRUE : Boolean.FALSE;
}and identical?
public final class user$fn_line_1__258 extends AFunction
{
public static Object invokeStatic(final Object x, final Object y) {
return Util.identical(x, y) ? Boolean.TRUE : Boolean.FALSE;
}let's benchmark this
I don't think it matters at all in a benchmark :)
even = is very very fast
hm, getting some of these now in SCI CI:
Caused by: java.lang.IllegalArgumentException: Can't call public method of non-public class: public abstract java.lang.Object java.util.function.Function.apply(java.lang.Object)https://github.com/babashka/sci/actions/runs/18984120787/job/54223745821
I must have screwed something up
ah yes, the "more simple" commit screwed it up
@@ -9,8 +9,10 @@
- FISupport - extracted from Compiler to support functional interface adaptation"
{:no-doc true}
#?(:clj
- (:import [java.lang.reflect Method Modifier Proxy]
- [clojure.lang Reflector Compiler IFn RT])))
+ (:import [java.lang.reflect Method Modifier]
+ [clojure.lang Reflector Compiler IFn RT]
+ [java.lang.invoke MethodHandles MethodType]
+ [java.lang.reflect Proxy])))
#?(:clj (set! *warn-on-reflection* :warn-on-boxed))
@@ -294,8 +296,15 @@
(let [^Method
accessible-m (if (or (not (Modifier/isPublic
(.getModifiers (.getDeclaringClass m))))
+ ;; Check canAccess on Java 9+
(and target
- (.canAccess m target)))
+ (try
+ (when-let [can-access-method
+ (try (.getMethod Method "canAccess"
+ (into-array Class [Object]))
+ (catch NoSuchMethodException _ nil))]
+ (not (.invoke ^Method can-access-method m (object-array [target]))))
+ (catch Exception _ false))))
(clojure.lang.Reflector/getAsMethodOfAccessibleBase (or context-class (.getDeclaringClass m))
m
target)(not (.canAccesss ...)) ah
forgot a not
I think I can get rid of the borkdude/locking library as well, don't know if you ran into that
didn't need it for cljr so I just dropped it
it was a workaround for an issue in clojure
back in the day
I made a new PR here now. https://github.com/babashka/sci/pull/1008
> I'll take a few minutes later today to step through each line to ensure it's transliterated faithfully if you can review that later today I'll merge and bump in bb
Good and bad news. I reviewed the wrong PR, and I'm totally clocked out, but I got to the end https://github.com/babashka/sci/pull/1007#pullrequestreview-3406268482
I found only one major issue. There's a place where we call (widen-boxed-args args) and we need to use it to shadow the original args but we don't. https://github.com/babashka/sci/pull/1007#discussion_r2482785595
I ported over the helpful comments and suggestions to refactor to be less opaque.
But now that I reconsider, widen-boxed-args probably mutates its arg, so maybe its fine.
My brain is fried!! talk later
Only 2 open comments, rest is resolved in the other PR
I made a mistake somewhere along processing the comments. Will try to find the bug tomorrow
ah darn, it's lein do clean, test :only babashka.interop-test. I forgot clean, this is why it didn't work locally
no, still something's failing. I introduced something stupid somewhere.
ERROR in (SSL-test) (test_utils.clj:82)
expected: (= :user/success (bb nil "(try (.createSocket (javax.net.ssl.SSLSocketFactory/getDefault) \"localhost\" 4444) (catch java.net.ConnectException e ::success))"))
actual: clojure.lang.ExceptionInfo: ----- Error --------------------------------------------------------------------
Type: java.lang.reflect.InvocationTargetException(this is in a bb test)
oh lol, it turns out you do need the sneakythrow.
(try
(let [ret (.invoke accessible-m target (box-args (.getParameterTypes accessible-m) args))]
(Reflector/prepRet (.getReturnType accessible-m) ret))
(catch Exception e
(throw (clojure.lang.Util/sneakyThrow
(or (.getCause e) e)))))when I remove it tests fail
with the wrong error
all good now. I also made widen-box-args mutate the original array since it was reassigned anyway
hmm, no that's not clean, I'm not sure if that will have an effect on the original caller...
yeah that's actually fine I think