Skip to content

Commit f7594d5

Browse files
committed
[new] Refactor deftype field serialization
1. Adds support for private (e.g. ^:unsynchronized-mutable) fields 2. Adds caching for improved performance
1 parent aa601f1 commit f7594d5

File tree

2 files changed

+50
-52
lines changed

2 files changed

+50
-52
lines changed

src/taoensso/nippy.clj

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,25 +1132,28 @@
11321132

11331133
(-freeze-without-meta! (into {} x) out)))
11341134

1135-
(let [munged-name (enc/fmemoize #(munge (name %)))
1136-
get-basis
1137-
(do #_enc/fmemoize ; Small perf benefit not worth the loss of dynamism
1138-
(fn [^java.lang.Class aclass]
1139-
(let [basis-method (.getMethod aclass "getBasis" nil)]
1140-
(.invoke basis-method nil nil))))]
1141-
1142-
(freezer IType nil true
1143-
(let [aclass (class x)
1144-
class-name (.getName aclass)]
1145-
(write-id out id-type)
1146-
(write-str out class-name)
1147-
(-run!
1148-
(fn [b]
1149-
(let [^Field cfield (.getField aclass (munged-name b))]
1150-
(-freeze-without-meta! (.get cfield x) out)))
1151-
(get-basis aclass)))))
1152-
1153-
(comment (do (deftype T1 [x]) (.invoke (.getMethod (class (T1. :x)) "getBasis" nil) nil nil)))
1135+
(def ^:private get-basis-fields
1136+
"Returns [`java.lang.reflect.Field` ...] for given class."
1137+
(enc/fmemoize
1138+
(fn [^Class c] ; Auto invalidated on `deftype` redef, etc.
1139+
(let [basis (.invoke (.getMethod c "getBasis" nil) nil nil)]
1140+
(mapv
1141+
(fn [f]
1142+
(let [field (.getDeclaredField c (munge (name f)))]
1143+
(.setAccessible field true)
1144+
(do field)))
1145+
basis)))))
1146+
1147+
(freezer IType nil true
1148+
(let [c (class x)]
1149+
(write-id out id-type)
1150+
(write-str out (.getName c))
1151+
(-run! (fn [^java.lang.reflect.Field f] (-freeze-without-meta! (.get f x) out))
1152+
(get-basis-fields c))))
1153+
1154+
(comment
1155+
(do (deftype T1 [x]) (let [t1 (T1. :x)] (get-basis-fields (class t1))))
1156+
(do (deftype T2 [^:unsynchronized-mutable x]) (let [t2 (T2. :x)] (get-basis-fields (class t2)))))
11541157

11551158
(enc/compile-if java.time.Instant
11561159
(freezer java.time.Instant id-time-instant true
@@ -1404,8 +1407,6 @@
14041407

14051408
(defn- read-kvs-depr [to ^DataInput in] (read-kvs-into to in (quot (.readInt in) 2)))
14061409

1407-
(def ^:private class-method-sig (into-array Class [IPersistentMap]))
1408-
14091410
(defn- read-custom! [in prefixed? type-id]
14101411
(if-let [custom-reader (get *custom-readers* type-id)]
14111412
(try
@@ -1497,39 +1498,36 @@
14971498
"Cannot thaw object: `taoensso.nippy/*thaw-serializable-allowlist*` check failed. This is a security feature. See `*thaw-serializable-allowlist*` docstring or https://github.com/ptaoussanis/nippy/issues/130 for details!"
14981499
{:class-name class-name})))
14991500

1500-
(defn- read-record [in class-name]
1501-
(let [content (thaw-from-in! in)]
1502-
(try
1503-
(let [class (clojure.lang.RT/classForName class-name)
1504-
method (.getMethod class "create" class-method-sig)]
1505-
(.invoke method class (into-array Object [content])))
1506-
(catch Exception e
1507-
{:nippy/unthawable
1508-
{:type :record
1509-
:cause :exception
1501+
(let [class-method-sig (into-array Class [IPersistentMap])]
1502+
(defn- read-record [in class-name]
1503+
(let [content (thaw-from-in! in)]
1504+
(try
1505+
(let [c (clojure.lang.RT/classForName class-name)
1506+
ctr (.getMethod c "create" class-method-sig)]
1507+
(.invoke ctr c (into-array Object [content])))
1508+
(catch Exception e
1509+
{:nippy/unthawable
1510+
{:type :record
1511+
:cause :exception
15101512

1511-
:class-name class-name
1512-
:content content
1513-
:exception e}}))))
1513+
:class-name class-name
1514+
:content content
1515+
:exception e}})))))
15141516

15151517
(defn- read-type [in class-name]
15161518
(try
1517-
(let [aclass (clojure.lang.RT/classForName class-name)
1518-
nbasis
1519-
(let [basis-method (.getMethod aclass "getBasis" nil)
1520-
basis (.invoke basis-method nil nil)]
1521-
(count basis))
1519+
(let [c (clojure.lang.RT/classForName class-name)
1520+
num-fields (count (get-basis-fields c))
1521+
field-vals (object-array num-fields)
15221522

1523-
cvalues (object-array nbasis)]
1523+
;; Ref. <https://github.com/clojure/clojure/blob/e78519c174fb506afa70e236af509e73160f022a/src/jvm/clojure/lang/Compiler.java#L4799>
1524+
^Constructor ctr (aget (.getConstructors c) 0)]
15241525

15251526
(enc/reduce-n
1526-
(fn [_ i] (aset cvalues i (thaw-from-in! in)))
1527-
nil nbasis)
1527+
(fn [_ i] (aset field-vals i (thaw-from-in! in)))
1528+
nil num-fields)
15281529

1529-
(let [ctors (.getConstructors aclass)
1530-
^Constructor ctor (aget ctors 0) ; Impl. detail? Ref. <https://goo.gl/XWmckR>
1531-
]
1532-
(.newInstance ctor cvalues)))
1530+
(.newInstance ctr field-vals))
15331531

15341532
(catch Exception e
15351533
{:nippy/unthawable
@@ -1997,10 +1995,10 @@
19971995

19981996
;;;; Stress data
19991997

2000-
(defrecord StressRecord [my-data])
2001-
(deftype StressType [my-data]
2002-
Object (equals [a b] (and (instance? StressType b) (= (.-my-data a)
2003-
(.-my-data ^StressType b)))))
1998+
(defrecord StressRecord [x])
1999+
(deftype StressType [x ^:unsynchronized-mutable y]
2000+
clojure.lang.IDeref (deref [_] [x y])
2001+
Object (equals [_ other] (and (instance? StressType other) (= [x y] @other))))
20042002

20052003
(defn stress-data
20062004
"Returns map of reference stress data for use by tests, benchmarks, etc."
@@ -2054,7 +2052,7 @@
20542052
:uuid (java.util.UUID. 7232453380187312026 -7067939076204274491)
20552053
:uri (java.net.URI. "https://clojure.org")
20562054
:defrecord (StressRecord. "data")
2057-
:deftype (StressType. "data")
2055+
:deftype (StressType. "normal field" "private field")
20582056

20592057
:util-date (java.util.Date. 1577884455500)
20602058
:sql-date (java.sql.Date. 1577884455500)

test/taoensso/nippy_tests.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@
203203
(defn gen-hashes [] (enc/map-vals (fn [v] (ba-hash (freeze v))) test-data))
204204
(defn cmp-hashes [new old] (vec (sort (reduce-kv (fn [s k v] (if (= (get old k) v) s (conj s k))) #{} new))))
205205

206-
(def ref-hashes {:deftype -148586793, :lazy-seq-empty 1277437598, :true -1809580601, :long 598276629, :double -454270428, :lazy-seq -1039619789, :short 1152993378, :meta -858252893, :str-long -1970041891, :instant -1401948864, :many-keywords 665654816, :bigint 2033662230, :sym-ns 769802402, :queue 447747779, :float 603100813, :sorted-set 2005004017, :many-strings 1738215727, :nested -1350538572, :queue-empty 1760934486, :duration -775528642, :false 1506926383, :vector 813550992, :util-date 1326218051, :kw 389651898, :sym -1742024487, :str-short -921330463, :subvec 709331681, :kw-long 852232872, :integer 624865727, :sym-long -1535730190, :list -1207486853, :ratio 1186850097, :byte -1041979678, :bigdec -1846988137, :nil 2005042235, :defrecord -553848560, :sorted-map -1160380145, :sql-date 80018667, :map-entry 1219306839, :false-boxed 1506926383, :uri 870148616, :period -2043530540, :many-longs -1109794519, :uuid -338331115, :set 1649942133, :kw-ns 1050084331, :map 1989337680, :many-doubles -827569787, :char 858269588})
206+
(def ref-hashes {:deftype -671450876, :lazy-seq-empty 1277437598, :true -1809580601, :long 598276629, :double -454270428, :lazy-seq -1039619789, :short 1152993378, :meta -858252893, :str-long -1970041891, :instant -1401948864, :many-keywords 665654816, :bigint 2033662230, :sym-ns 769802402, :queue 447747779, :float 603100813, :sorted-set 2005004017, :many-strings 1738215727, :nested -1350538572, :queue-empty 1760934486, :duration -775528642, :false 1506926383, :vector 813550992, :util-date 1326218051, :kw 389651898, :sym -1742024487, :str-short -921330463, :subvec 709331681, :kw-long 852232872, :integer 624865727, :sym-long -1535730190, :list -1207486853, :ratio 1186850097, :byte -1041979678, :bigdec -1846988137, :nil 2005042235, :defrecord 842721251, :sorted-map -1160380145, :sql-date 80018667, :map-entry 1219306839, :false-boxed 1506926383, :uri 870148616, :period -2043530540, :many-longs -1109794519, :uuid -338331115, :set 1649942133, :kw-ns 1050084331, :map 1989337680, :many-doubles -827569787, :char 858269588})
207207

208208
(comment (cmp-hashes (gen-hashes) ref-hashes)) ; []
209209

0 commit comments

Comments
 (0)