Skip to content

Instantly share code, notes, and snippets.

@seancorfield
Last active June 10, 2020 20:33
Show Gist options
  • Save seancorfield/01e42dbf3de68bab6ef3d7d6245af192 to your computer and use it in GitHub Desktop.
Save seancorfield/01e42dbf3de68bab6ef3d7d6245af192 to your computer and use it in GitHub Desktop.
Debugging Reflection Warnings
;; I use Clojure CLI / deps.edn and I have these JVM options in an alias
;; that use when starting my REPL for testing:
:jvm-opts ["-Dclojure.core.async.go-checking=true"
"-XX:-OmitStackTraceInFastThrow"
"--illegal-access=warn"]
;; the last one is relevant to this particular piece of debugging so that
;; _all_ illegal reflective access warnings are shown, not just the last
;;
;; the stacktrace option ensures the JVM doesn't optimize away stack traces
;; for commonly thrown exceptions (such as NPE)
;;
;; the core.async option checks that you don't do blocking ops inside go loops
;; here's my REPL session showing how I debugged it (I run OpenJDK 14 locally)
;;
;; first off, test the original function as-is:
user=> (import '(java.text BreakIterator))
java.text.BreakIterator
user=> (def grapheme-length
#(->> (doto (BreakIterator/getCharacterInstance)
(.setText %))
(partial (memfn next))
repeatedly
(take-while (partial not= BreakIterator/DONE))
count))
#'user/grapheme-length
user=> (grapheme-length "\uD83D\uDE04⌘\uD83D\uDE00")
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.setText(java.text.CharacterIterator)
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
3
;; address .setText -- https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/BreakIterator.html
;; overloaded on String and CharacterIterator so tell it we want the String version:
user=> (def grapheme-length
#(->> (doto (BreakIterator/getCharacterInstance)
(.setText ^String %))
(partial (memfn next))
repeatedly
(take-while (partial not= BreakIterator/DONE))
count))
#'user/grapheme-length
user=> (grapheme-length "\uD83D\uDE04⌘\uD83D\uDE00")
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
WARNING: Illegal reflective access ... method sun.text.RuleBasedBreakIterator.next()
3
;; replace memfn with an explicit function so we can type hint the call:
user=> (def grapheme-length
#(->> (doto (BreakIterator/getCharacterInstance)
(.setText ^String %))
(partial (fn [o] (.next o)))
repeatedly
(take-while (partial not= BreakIterator/DONE))
count))
#'user/grapheme-length
;; omitting confirmation that the call still produces the same four warnings
;; now add type hint on o:
user=> (def grapheme-length
#(->> (doto (BreakIterator/getCharacterInstance)
(.setText ^String %))
(partial (fn [^BreakIterator o] (.next o)))
repeatedly
(take-while (partial not= BreakIterator/DONE))
count))
#'user/grapheme-length
user=> (grapheme-length "\uD83D\uDE04⌘\uD83D\uDE00")
3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment