Welcome! Please see the About page for a little more info on how this works.

0 votes
in Java Interop by

Attempting to get the length of a Unicode string, I found this code on RosettaCode [1] called grapheme-length :

(def grapheme-length
  #(->> (doto (BreakIterator/getCharacterInstance)
          (.setText %))
        (partial (memfn next))
        (take-while (partial not= BreakIterator/DONE))

I invoked it from the REPL to test it like this:

(star-traders.util/grapheme-length "\uD83D\uDE04⌘\uD83D\uDE00") ; Expecting 3

While it did return the expected value, I also got an unexpected warning in red text asking me to "consider reporting this to the maintainers of clojure.lang.InjectedInvoker"...

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by clojure.lang.InjectedInvoker/0x0000000800cc8440 (file:/Users/myusername/.m2/repository/org/clojure/clojure/1.10.1/clojure-1.10.1.jar) to method sun.text.RuleBasedBreakIterator.setText(java.text.CharacterIterator)
WARNING: Please consider reporting this to the maintainers of clojure.lang.InjectedInvoker/0x0000000800cc8440
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Apparently this is a known issue with the JVM due to the addition of the Java 9 module system and is mentioned in the Clojure FAQ. [2]

While the problem does go away if --illegal-access=deny is added to the JVM Args, it appears that the recommended solution is to fix the code with type hinting.

I've tried using --illegal-access=warn to locate the spot and fix it, like this:

          (.setText ^java.text.BreakIterator %)) ; Problem happens here (I think)

However, the problem still occurs, and this has me curious what the right type I should be using is.

How do I fix the code to get rid of the compiler warning, rather than suppress warnings?

[1] https://rosettacode.org/wiki/String_length#Grapheme_Length_2
[2] https://clojure.org/guides/faq#illegal_access

1 Answer

+1 vote
selected by
Best answer

There are actually reflection warnings coming from a couple of lines in this: the .setText call and the .next call. I believe you need the following to resolve both of them:

user=> (def grapheme-length
  #(->> (doto (BreakIterator/getCharacterInstance)
          (.setText ^String %)) ; hint the argument here
        (partial (fn [^BreakIterator o] (.next o))) ; hint the object here
        (take-while (partial not= BreakIterator/DONE))
user=> (grapheme-length "\uD83D\uDE04⌘\uD83D\uDE00")
Just in general, memfn will always introduce reflection, just by the nature of its impl.
This did the trick, Sean. Thank you.  I'd be curious how you figured this out, but that's a conversation for another day. I suspect I wasn't groking what the error messages were telling me.  (Thanks, also, Alex -- this was my first encounter with memfn.)
I just copied my REPL session into a Gist and annotated it: https://gist.github.com/seancorfield/01e42dbf3de68bab6ef3d7d6245af192