smalltalk: method-lookup cache + 10 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
This commit is contained in:
@@ -17,7 +17,43 @@
|
||||
|
||||
(define st-class-table {})
|
||||
|
||||
(define st-class-table-clear! (fn () (set! st-class-table {})))
|
||||
;; ── Method-lookup cache ────────────────────────────────────────────────
|
||||
;; Cache keys are "class|selector|side"; side is "i" (instance) or "c" (class).
|
||||
;; Misses are stored as the sentinel :not-found so we don't re-walk for
|
||||
;; every doesNotUnderstand call.
|
||||
(define st-method-cache {})
|
||||
(define st-method-cache-hits 0)
|
||||
(define st-method-cache-misses 0)
|
||||
|
||||
(define
|
||||
st-method-cache-clear!
|
||||
(fn () (set! st-method-cache {})))
|
||||
|
||||
(define
|
||||
st-method-cache-key
|
||||
(fn (cls sel class-side?) (str cls "|" sel "|" (if class-side? "c" "i"))))
|
||||
|
||||
(define
|
||||
st-method-cache-stats
|
||||
(fn
|
||||
()
|
||||
{:hits st-method-cache-hits
|
||||
:misses st-method-cache-misses
|
||||
:size (len (keys st-method-cache))}))
|
||||
|
||||
(define
|
||||
st-method-cache-reset-stats!
|
||||
(fn ()
|
||||
(begin
|
||||
(set! st-method-cache-hits 0)
|
||||
(set! st-method-cache-misses 0))))
|
||||
|
||||
(define
|
||||
st-class-table-clear!
|
||||
(fn ()
|
||||
(begin
|
||||
(set! st-class-table {})
|
||||
(st-method-cache-clear!))))
|
||||
|
||||
(define
|
||||
st-class-define!
|
||||
@@ -34,6 +70,9 @@
|
||||
:ivars ivars
|
||||
:methods {}
|
||||
:class-methods {}}))
|
||||
;; A redefined class can invalidate any cache entries that walked
|
||||
;; through its old position in the chain. Cheap + correct: drop all.
|
||||
(st-method-cache-clear!)
|
||||
name)))
|
||||
|
||||
(define
|
||||
@@ -114,6 +153,7 @@
|
||||
cls
|
||||
:methods
|
||||
(assoc (get cls :methods) selector m))))
|
||||
(st-method-cache-clear!)
|
||||
selector)))))))
|
||||
|
||||
(define
|
||||
@@ -137,13 +177,43 @@
|
||||
cls
|
||||
:class-methods
|
||||
(assoc (get cls :class-methods) selector m))))
|
||||
(st-method-cache-clear!)
|
||||
selector)))))))
|
||||
|
||||
;; Method lookup: walk superclass chain starting at `cls-name`.
|
||||
;; class-side? = true searches :class-methods, false searches :methods.
|
||||
;; Returns the method record (with :defining-class) or nil.
|
||||
;; Remove a method from a class (instance side). Mostly for tests; runtime
|
||||
;; reflection in Phase 4 will use the same primitive.
|
||||
(define
|
||||
st-method-lookup
|
||||
st-class-remove-method!
|
||||
(fn
|
||||
(cls-name selector)
|
||||
(let ((cls (st-class-get cls-name)))
|
||||
(cond
|
||||
((= cls nil) (error (str "st-class-remove-method!: unknown class " cls-name)))
|
||||
(else
|
||||
(let ((md (get cls :methods)))
|
||||
(cond
|
||||
((not (has-key? md selector)) false)
|
||||
(else
|
||||
(let ((new-md {}))
|
||||
(begin
|
||||
(for-each
|
||||
(fn (k)
|
||||
(when (not (= k selector))
|
||||
(dict-set! new-md k (get md k))))
|
||||
(keys md))
|
||||
(set!
|
||||
st-class-table
|
||||
(assoc
|
||||
st-class-table
|
||||
cls-name
|
||||
(assoc cls :methods new-md)))
|
||||
(st-method-cache-clear!)
|
||||
true))))))))))
|
||||
|
||||
;; Walk-only lookup. Returns the method record (with :defining-class) or nil.
|
||||
;; class-side? = true searches :class-methods, false searches :methods.
|
||||
(define
|
||||
st-method-lookup-walk
|
||||
(fn
|
||||
(cls-name selector class-side?)
|
||||
(let
|
||||
@@ -165,6 +235,32 @@
|
||||
(ml-loop cls-name)
|
||||
found))))
|
||||
|
||||
;; Cached lookup. Misses are stored as :not-found so doesNotUnderstand paths
|
||||
;; don't re-walk on every send.
|
||||
(define
|
||||
st-method-lookup
|
||||
(fn
|
||||
(cls-name selector class-side?)
|
||||
(let ((key (st-method-cache-key cls-name selector class-side?)))
|
||||
(cond
|
||||
((has-key? st-method-cache key)
|
||||
(begin
|
||||
(set! st-method-cache-hits (+ st-method-cache-hits 1))
|
||||
(let ((v (get st-method-cache key)))
|
||||
(cond ((= v :not-found) nil) (else v)))))
|
||||
(else
|
||||
(begin
|
||||
(set! st-method-cache-misses (+ st-method-cache-misses 1))
|
||||
(let ((found (st-method-lookup-walk cls-name selector class-side?)))
|
||||
(begin
|
||||
(set!
|
||||
st-method-cache
|
||||
(assoc
|
||||
st-method-cache
|
||||
key
|
||||
(cond ((= found nil) :not-found) (else found))))
|
||||
found))))))))
|
||||
|
||||
;; SX value → Smalltalk class name. Native types are not boxed.
|
||||
(define
|
||||
st-class-of
|
||||
|
||||
Reference in New Issue
Block a user