;; Inline-cache tests — verify the per-call-site IC slot fires on hot
;; sends and is invalidated by class-table mutations.

(set! st-test-pass 0)
(set! st-test-fail 0)
(set! st-test-fails (list))

(st-bootstrap-classes!)
(define ev (fn (src) (smalltalk-eval src)))
(define evp (fn (src) (smalltalk-eval-program src)))

;; ── 1. Counters exist ──
(st-test "stats has :hits"   (has-key? (st-ic-stats) :hits) true)
(st-test "stats has :misses" (has-key? (st-ic-stats) :misses) true)
(st-test "stats has :gen"    (has-key? (st-ic-stats) :gen) true)

;; ── 2. Repeated send to user method hits the IC ──
(st-class-define! "Pinger" "Object" (list))
(st-class-add-method! "Pinger" "ping" (st-parse-method "ping ^ #pong"))

;; Important: the IC is keyed on the AST node, so a single call site
;; invoked many times via a loop is what produces hits. Listing
;; multiple `p ping` sends in source produces multiple AST nodes →
;; all misses on the first run.
(st-ic-reset-stats!)
(evp "| p | p := Pinger new.
      1 to: 10 do: [:i | p ping]")

(define ic-after-loop (st-ic-stats))
(st-test "loop-driven sends produce hits"
  (> (get ic-after-loop :hits) 0) true)
(st-test "first iteration is a miss"
  (>= (get ic-after-loop :misses) 1) true)

;; ── 3. Different receiver class causes a miss ──
(st-class-define! "Cooer" "Object" (list))
(st-class-add-method! "Cooer" "ping" (st-parse-method "ping ^ #coo"))

(st-ic-reset-stats!)
(evp "| p c |
      p := Pinger new.
      c := Cooer new.
      ^ {p ping. c ping. p ping. c ping}")
;; First p ping → miss. c ping with same call site → miss (class changed).
;; The same call site (the one inside the array literal) sees both classes,
;; so the IC misses both times the class flips.
(define ic-mixed (st-ic-stats))
(st-test "polymorphic call site has misses"
  (>= (get ic-mixed :misses) 2) true)

;; ── 4. Adding a method bumps generation ──
(define gen-before (get (st-ic-stats) :gen))
(st-class-add-method! "Pinger" "echo" (st-parse-method "echo ^ #echo"))
(define gen-after (get (st-ic-stats) :gen))

(st-test "method add bumped generation"
  (> gen-after gen-before) true)

;; ── 5. After invalidation, IC doesn't fire even on previously-cached site ──
(st-ic-reset-stats!)
(evp "| p | p := Pinger new. ^ p ping")  ;; warm
(evp "| p | p := Pinger new. ^ p ping")  ;; should hit
(st-class-add-method! "Pinger" "ping" (st-parse-method "ping ^ #newPong"))
(evp "| p | p := Pinger new. ^ p ping")  ;; should miss after invalidate

(define ic-final (st-ic-stats))
(st-test "post-invalidation send is a miss"
  (>= (get ic-final :misses) 2) true)

(st-test "the new method is what fires"
  (str (evp "^ Pinger new ping"))
  "newPong")

;; ── 6. Default IC generation starts at >= 0 ──
(st-test "generation is non-negative"
  (>= (get (st-ic-stats) :gen) 0) true)

(list st-test-pass st-test-fail)
