(ns com.tailrecursion.blacktide.interop-values-test
"Method values and adapters retain their behavior across recovered control.
Bound references preserve identity; generated wrapper classes may differ."
(:require [clojure.test :refer [deftest is]]
[com.tailrecursion.blacktide.compiler.passes.structure :as structure]
[com.tailrecursion.blacktide.backend-fixture :as backend]
[com.tailrecursion.blacktide.jvm-fixture]))
(def flags [true false false true])
(deftest java-function-values-across-joins
(let [target (proxy [java.util.HashMap] [] (compute [_ callback] callback))
callback (fn [x y] [x y])]
(doseq [[tail invoke]
[['String/.length #(% "abc")]
[(with-meta 'String/valueOf {:param-tags ['Object]}) #(% "abc")]
[(with-meta 'String/new {:param-tags ['String]}) #(% "abc")]
['(let [^java.util.function.BiFunction f callback] f)
#(.apply ^java.util.function.BiFunction % :left :right)]
['(java.util.Map/.compute target :key callback)
#(.apply ^java.util.function.BiFunction % :left :right)]
['(com.tailrecursion.blacktide.jvm-fixture/map-compute target :key callback)
#(.apply ^java.util.function.BiFunction % :left :right)]]
mode [:default :dispatcher]]
(let [ordinary (list 'let ['values '(if flag [1 2] [3 4])] (list 'vector 'values tail))
compiled (list 'loophole '[a 0 b 0]
'(if flag (setq a 1 b 2) (setq a 3 b 4)) (list 'vector '[a b] tail))
observe (fn [body mode]
(let [f ((backend/compile-program (list 'fn '[flag target callback] body) mode) nil)
results (mapv #(f % target callback) flags)]
(mapv (fn [[pair f]] [pair (invoke f)]) results)))]
(is (= :structured (:op (backend/capture #'structure/recover
(list 'fn '[flag target callback] compiled)))))
(is (= (observe ordinary :ordinary) (observe compiled mode)) (pr-str [mode tail]))))))
(deftest bound-adapters-retain-identity-and-effects
(doseq [mode [:default :dispatcher] flag [false true]]
(let [events (atom []) callback (fn [x y] (swap! events conj [x y]) (+ x y))
f ((backend/compile-program
'(fn [flag callback]
(loophole [a 0 b 0 saved nil]
(if flag (setq a 1 b 2) (setq a 3 b 4))
(let [^java.util.function.BiFunction f (do (swap! events conj :adapt) callback)]
(setq saved f)
[(identical? saved f) (.apply f a b) saved]))) mode) events)
[same? result adapter] (f flag callback)]
(is same?)
(is (= (if flag 3 7) result))
(is (= 9 (.apply ^java.util.function.BiFunction adapter 4 5)))
(is (= [:adapt (if flag [1 2] [3 4]) [4 5]] @events)))))