648648
649649(deftest test-boxing-prevention-when-compiling-statements
650650 (is (= 1 (.get (doto (AtomicInteger. 0 ) inc-atomic-int))))
651- (is (= 1 (.get (doto (AtomicLong. 0 ) inc-atomic-long)))))
651+ (is (= 1 (.get (doto (AtomicLong. 0 ) inc-atomic-long)))))
652+
653+ (deftest array-type-symbols
654+ (is (= long/1 (class (make-array Int64 0 )))) ; ;; Long/TYPE
655+ (is (= int/1 (class (make-array Int32 0 )))) ; ;; Integer/TYPE
656+ (is (= double/1 (class (make-array Double 0 )))) ; ;; Double/TYPE
657+ (is (= short/1 (class (make-array Int16 0 )))) ; ;; Short/TYPE
658+ (is (= boolean/1 (class (make-array Boolean 0 )))) ; ;; Boolean/TYPE
659+ (is (= byte/1 (class (make-array Byte 0 )))) ; ;; Byte/TYPE
660+ (is (= float/1 (class (make-array Single 0 )))) ; ;; Float/TYPE
661+ (is (= String/1 (class (make-array String 0 ))))
662+ (is (= System.String/1 (class (make-array String 0 )))) ; ;; java.lang.String
663+ (is (= System.Guid/1 (class (make-array System.Guid 0 )))) ; ;; java.util.UUID java.util.UUID
664+ (is (= `byte/1 'byte/1 ))
665+ (is (= `byte/3 'byte/3 ))
666+ (is (= `System.Guid/1 'System.Guid/1 )) ; ;; java.util.UUID java.util.UUID
667+ (is (= `String/1 'System.String/1 )) ; ;; java.lang.String
668+ (is (= `System.String/1 'System.String/1 )) ; ;; java.lang.String java.lang.String
669+ (is (= ['long/2 ] `[~'long/2 ])))
670+
671+ (comment " Mostly Java FI"
672+ (defn make-test-files []
673+ (let [id (str (UUID/randomUUID ))
674+ temp-1 (java.io.File/createTempFile (str " test-1-" id)" .edn" )
675+ temp-2 (java.io.File/createTempFile " test-2" " .xml" )
676+ temp-3 (java.io.File/createTempFile (str " test-3-" id)" .edn" )
677+ dir (File. (.getParent temp-3))]
678+ {:dir dir :file-id id}))
679+
680+ (defn return-long ^long []
681+ (let [^java.util.function.ToLongFunction f (fn ^long [x] 1 )]
682+ (Long/highestOneBit (.applyAsLong f :x ))))
683+
684+ (deftest clojure-fn-as-java-fn
685+ ; ; pass Clojure fn as Java Predicate
686+ (let [coll (java.util.ArrayList. [1 2 3 4 5 ])]
687+ (is (true ? (.removeIf coll even?)))
688+ (is (= coll [1 3 5 ])))
689+
690+ ; ; binding type hint triggers coercion
691+ (is (instance? FileFilter
692+ (let [^FileFilter ff (fn [f] true )] ff)))
693+
694+ ; ; coercion in let - reflection has types that should work
695+ (let [{:keys [dir file-id]} (make-test-files )
696+ ^FileFilter ff (fn [^File f]
697+ (str/includes? (.getName f) file-id))
698+ filtered (.listFiles dir ff)]
699+ (is (= 2 (count filtered))))
700+
701+ ; ; coercion in let
702+ (let [{:keys [dir file-id]} (make-test-files )
703+ ^FileFilter ff (fn [^File f]
704+ (str/includes? (.getName f) file-id))
705+ filtered (.listFiles ^File dir ff)]
706+ (is (= 2 (count filtered))))
707+
708+ ; ;; resolve method ambiguity using member symbol and param-tags
709+ (let [{:keys [dir file-id]} (make-test-files )
710+ ^FileFilter ff (fn [^File f]
711+ (str/includes? (.getName f) file-id))
712+ filtered (^[FileFilter] File/.listFiles dir ff)]
713+ (is (= 2 (count filtered))))
714+
715+ (defn files-with-ext [^File dir ext]
716+ (vec (.list dir ^FilenameFilter #(str/ends-with? % ext))))
717+
718+ (let [{:keys [dir file-id]} (make-test-files )
719+ ^FilenameFilter ff (fn [dir file-name]
720+ (str/includes? file-name file-id))
721+ filtered (.list ^File dir ff)]
722+ (is (= 2 (count filtered))))
723+
724+ (let [^java.util.function.DoubleToLongFunction f (fn [d] (int d))]
725+ (is (instance? java.util.function.DoubleToLongFunction f))
726+ (is (= 10 (.applyAsLong f (double 10.6 )))))
727+
728+ (let [^java.util.function.IntConsumer f (fn [i] nil )]
729+ (is (nil? (.accept f 42 ))))
730+
731+ (let [^java.util.function.IntPredicate f (fn [i] true )]
732+ (is (true ? (.test f 42 ))))
733+
734+ (let [arr (java.util.ArrayList. [1 2 3 4 5 ])
735+ ^java.util.function.ObjDoubleConsumer f (fn [arr i] nil )]
736+ (is (nil? (.accept f arr 42 ))))
737+
738+ (let [f (constantly 100 )
739+ ^Runnable g f]
740+ (is (identical? f g) " has been unintentionally adapted" ))
741+
742+ (let [^java.util.function.Predicate pred even?
743+ coll1 (java.util.ArrayList. [1 2 3 4 5 ])
744+ coll2 (java.util.ArrayList. [6 7 8 9 10 ])]
745+ (is (instance? java.util.function.Predicate pred))
746+ (is (true ? (.removeIf coll1 pred)))
747+ (is (= coll1 [1 3 5 ]))
748+ (is (true ? (.removeIf coll2 pred)))
749+ (is (= coll2 [7 9 ])))
750+
751+ (let [^java.util.function.Predicate pred even?
752+ coll1 (java.util.ArrayList. [1 2 3 4 5 ])
753+ cup-fn (java.util.ArrayList. [1 2 3 4 5 ])]
754+ (is (instance? java.util.function.Predicate pred))
755+ (is (true ? (.removeIf coll1 pred)))
756+ (is (= coll1 [1 3 5 ]))
757+ (is (true ? (.removeIf cup-fn pred)))
758+ (is (= cup-fn [1 3 5 ])))
759+
760+ (should-not-reflect #(clojure.test-clojure.java-interop/return-long ))
761+
762+ ; ; FI in class constructor
763+ (let [^java.util.function.Predicate hinted-pred (fn [i] (> i 0 ))
764+ clj-pred (fn [i] (> i 0 ))
765+ fi-constructor-1 (FIConstructor. hinted-pred)
766+ fi-constructor-2 (FIConstructor. clj-pred)
767+ fi-constructor-3 (FIConstructor. (fn [i] (> i 0 )))]
768+ (is (= [1 2 ] (.numbers fi-constructor-1)))
769+ (is (= [1 2 ] (.numbers fi-constructor-2)))
770+ (is (= [1 2 ] (.numbers fi-constructor-3))))
771+
772+ ; ; FI as arg to static
773+ (let [^java.util.function.Predicate hinted-pred (fn [i] (> i 0 ))
774+ res (FIStatic/numbers hinted-pred)]
775+ (is (= [1 2 ] res))))
776+
777+ (deftest eval-in-place-supplier-instance
778+ (def stream (java.util.stream.Stream/generate ^java.util.function.Supplier (atom 42 )))
779+ (is (instance? java.util.stream.Stream stream)))
780+
781+ (deftest eval-in-place-as-java-fn
782+ (def filtered-list (.removeIf (java.util.ArrayList. [1 2 3 4 5 ]) even?))
783+ (is (true ? filtered-list))
784+
785+ (def fi-constructor-numbers (.numbers (FIConstructor. (fn [i] (> i 0 )))))
786+ (is (= [1 2 ] fi-constructor-numbers))
787+
788+ (def fi-static (FIStatic/numbers (fn [i] (< i 0 ))))
789+ (is (= [-2 -1 ] fi-static)))
790+
791+ ; ; newDirectoryStream is overloaded, takes ^[Path String] or ^[Path DirectoryStream$Filter]
792+ ; ; so this method will reflect
793+ (defn get-dir-stream [^java.nio.file.Path dir-path glob-pattern]
794+ (let [path (.toPath (java.io.File. dir-path))]
795+ (java.nio.file.Files/newDirectoryStream path glob-pattern)))
796+
797+ (deftest test-reflection-to-overloaded-method-taking-FI
798+ ; ; all of these should resolve at runtime in reflection
799+ (is (not (nil? (get-dir-stream " ." " *" ))))
800+ (is (not (nil? (get-dir-stream " ." (reify java.nio.file.DirectoryStream$Filter (accept [_ path] (.isDirectory (.toFile path))))))))
801+ ; ; this one gets FI converted from IFn to DirectoryStream$Filter
802+ (is (not (nil? (get-dir-stream " ." (fn [^java.nio.file.Path path] (.isDirectory (.toFile path))))))))
803+
804+ ; ; we only support FI invoke coercion up to 10 args, this has 11
805+ (definterface ^{java.lang.FunctionalInterface true } FIWontWork
806+ (invoke [a b c d e f g h i j k]))
807+
808+ (definterface ReceivesFI
809+ (call [^clojure.test_clojure.java_interop.FIWontWork fi]))
810+
811+ (deftest test-reify-to-FI-allowed
812+ ; ; throws because there is no 11-arity invoker method and thus it is not possible to coerce
813+ (is (thrown? ClassCastException
814+ (eval '(let [^clojure.test_clojure.java_interop.FIWontWork f
815+ (fn [p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11] p11)]
816+ (.invoke f 1 2 3 4 5 6 7 8 9 10 11 )))))
817+
818+ (let [r (reify clojure.test_clojure.java_interop.ReceivesFI
819+ (call [_ fi] (.invoke fi 0 0 0 0 0 0 0 0 0 0 1 )))]
820+
821+ ; ; doesn't throw at compilation time, but throws at runtime
822+ ; ; because IFn cannot be implicitly converted
823+ (is (thrown? ClassCastException
824+ (.call r (fn [a b c d e f g h i j k] k))))
825+
826+ ; ; works because the reify implements the FI, no conversion necessary
827+ (is (= 1 (.call r (reify clojure.test_clojure.java_interop.FIWontWork (invoke [_ a b c d e f g h i j k] k)))))))
828+
829+ (definterface ^{java.lang.FunctionalInterface true } FIPrims
830+ (^long invoke [^long a ^long b ^long c ^long d]))
831+
832+ (definterface ReceivesFIPrims
833+ (call [^clojure.test_clojure.java_interop.FIPrims fi]))
834+
835+ (deftest test-match-prim-args-only-to-2
836+ (let [r (reify clojure.test_clojure.java_interop.ReceivesFIPrims
837+ (call [_ fi] (.invoke fi 1 2 3 4 )))]
838+ (is (= 4 (.call r (fn [^long a ^long b ^long c ^long d] d))))))
839+
840+ (deftest test-invoke-fiprim-rets
841+ (let [^clojure.test_clojure.java_interop.FIPrims f (fn [a b c d] a)]
842+ (is (instance? clojure.test_clojure.java_interop.FIPrims f))
843+ (is (= 1 (.invoke f 1 2 3 4 ))))
844+
845+ (is (= " LLL" (AdapterExerciser/.methodLLL (AdapterExerciser. ) (fn ^long [^long a ^long b]))))
846+ (is (= " OOOO" (AdapterExerciser/.methodOOOO (AdapterExerciser. ) (fn ^long [^long a ^long b ^long c]))))
847+ )
848+
849+ (deftest test-null-reify
850+ (is (= " null" ((fn [x] (FIStatic/allowsNullFI x)) nil ))))
851+
852+ (deftest test-FI-subtype
853+ (is (= [1 2 3 4 5 ] (->> (java.util.stream.Stream/iterate 1 inc) stream-seq! (take 5 )))))
854+
855+ (deftest class-methods-with-fi-args
856+ (testing " Constructor accepting FI arg, provided overloaded static class FI"
857+ (let [fi (FunctionalTester. " Constructor" 0 FunctionalTester/getChar)]
858+ (is (= \C (.testVar fi)))))
859+
860+ (testing " Instance method accepting FI arg, provided overloaded static class FI"
861+ (let [fi (FunctionalTester. " asf" 0 FunctionalTester/getChar)]
862+ (.instanceMethodWithFIArg fi " Instance" 0 FunctionalTester/getChar)
863+ (is (= \I (.testVar fi)))))
864+
865+ (testing " Static method accepting FI arg, provided overloaded static class FI"
866+ (is (= \S (FunctionalTester/staticMethodWithFIArg " Static" 0 FunctionalTester/getChar)))))
867+
868+ ; ; call is reflective and one overload takes an FI (Supplier)
869+ (definterface TakesFIOverloaded
870+ (call [^java.util.function.Supplier s])
871+ (call [^String s]))
872+
873+ (deftest CLJ-2898-reified-objs-both-IFn-and-FI
874+ ; ; f is both IFn and FI (Supplier)
875+ (let [f (reify
876+ java.util.function.Supplier
877+ (get [_] 100 )
878+
879+ clojure.lang.IFn
880+ (applyTo [_ _] 201 )
881+ (invoke [_] 200 ))]
882+
883+ ; ; should not be adapted. use Supplier.get() impl on tl
884+ (is (= 100 (.get (ThreadLocal/withInitial f))))
885+
886+ (let [tfio (reify TakesFIOverloaded
887+ (call [_ ^java.util.function.Supplier o] (.get o))
888+ (call [_ ^String s] " string" ))]
889+ ; ; reflective call to TakesFIOverloaded.call()
890+ ; ; as above, should not be adapted and use Supplier.get()
891+ (is (= 100 (.call tfio (identity f)))))))
892+ ) ; ; and commented out section
893+
894+ (deftest CLJ-2914-Qualified-Method-Expr-NPE
895+ (is (fails-with-cause? ArgumentException ; ;; IllegalArgumentException
896+ #"Malformed method expression.*String/\. length"
897+ (eval '(fn [] (System.String/.length )))))) ; ;; java.lang.String
0 commit comments