Skip to content

Commit 9cb236c

Browse files
committed
orchard.inspect: render non-accessible fields better
1 parent 895dff1 commit 9cb236c

File tree

4 files changed

+156
-159
lines changed

4 files changed

+156
-159
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
* `orchard.inspect`: don't render keyword/symbol/number values as strings.
1313
* `orchard.inspect`: don't use `pr-str` over the main `Value: ` being inspected.
1414
* All values are already formatted as strings, so this `pr-str` was redundant.
15+
* `orchard.inspect`: render non-accessible fields better.
16+
* If a given field cannot be inspected (because it's private and the JDK module system prevents opening it), we return the fixed symbol `<non-inspectable value>` for representing its value, clients being free to elide its rendering.
17+
* Now, for a given inspected Object (except Class objects), we return these sections, if present: `Instance fields`, `Static fields` ,`Private instance fields`, `Private static fields`.
18+
* For Class objects, we keep grouping the fields under a single `Fields` section.
19+
* `orchard.inspect`: render field names as symbols, not strings.
1520

1621
## 0.16.1 (2023-10-05)
1722

project.clj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
[org.clojure/clojure "1.12.0-master-SNAPSHOT" :classifier "sources"]]}
5252

5353

54-
:test {:dependencies [[org.clojure/java.classpath "1.0.0"]]
54+
:test {:dependencies [[org.clojure/java.classpath "1.0.0"]
55+
[nubank/matcher-combinators "3.8.8"]]
5556
:resource-paths ["test-resources"
5657
"not-a.jar"
5758
"does-not-exist.jar"]

src/orchard/inspect.clj

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -608,50 +608,51 @@
608608
(render-indent-str-lines obj)
609609
(unindent)))
610610

611+
(defn- field-val [^Field f, obj]
612+
(try
613+
(.get f obj)
614+
(catch Exception _
615+
::access-denied)))
616+
611617
(defmethod inspect :default [inspector obj]
612618
(let [class-chain (loop [c (class obj), res ()]
613619
(if c
614620
(recur (.getSuperclass c) (cons c res))
615621
res))
622+
memoized-field-val (memoize field-val)
616623
all-fields (mapcat #(.getDeclaredFields ^Class %) class-chain)
617-
618-
{static true, non-static false}
619-
(group-by #(Modifier/isStatic (.getModifiers ^Field %)) all-fields)]
620-
(letfn [(field-name [^Field f]
621-
(.getName f))
622-
623-
(field-val [^Field f]
624-
(let [^Exception e
625-
(try (.setAccessible f true)
626-
nil
627-
(catch Exception e
628-
;; We want to handle specifically SecurityException
629-
;; and j.l.r.InaccessibleObjectException, but the
630-
;; latter only comes with Java9+, so let's just
631-
;; catch everything instead.
632-
e))]
633-
(try (.get f obj)
634-
(catch java.lang.IllegalAccessException _
635-
(symbol
636-
(format "<Access denied%s>"
637-
(when e (str " (" (.getName (.getClass e)) ")"))))))))
638-
639-
(render-fields [inspector section-name fields]
624+
{static-accessible [true true]
625+
non-static-accessible [false true]
626+
static-nonaccessible [true false]
627+
non-static-nonaccessible [false false]}
628+
(group-by (fn [^Field f]
629+
[(Modifier/isStatic (.getModifiers f))
630+
(not= ::access-denied (memoized-field-val f obj))])
631+
all-fields)]
632+
(letfn [(render-fields [inspector section-name fields]
640633
(if (seq fields)
641634
(-> inspector
642635
(render-section-header section-name)
643636
(indent)
644637
(render-map-values (->> fields
645-
(map (fn [f] [(field-name f) (field-val f)]))
638+
(map (fn [^Field f]
639+
(let [v (memoized-field-val f obj)]
640+
[(-> f .getName symbol)
641+
(if (= v ::access-denied)
642+
;; This is a special value that can be detected client-side:
643+
(symbol "<non-inspectable value>")
644+
v)])))
646645
(into (sorted-map))))
647646
(unindent))
648647
inspector))]
649-
(-> inspector
650-
(render-labeled-value "Class" (class obj))
651-
(render-labeled-value "Value" obj)
652-
(render-fields "Fields" non-static)
653-
(render-fields "Static fields" static)
654-
(render-datafy obj)))))
648+
(cond-> inspector
649+
true (render-labeled-value "Class" (class obj))
650+
true (render-labeled-value "Value" obj)
651+
(seq non-static-accessible) (render-fields "Instance fields" non-static-accessible)
652+
(seq static-accessible) (render-fields "Static fields" static-accessible)
653+
(seq non-static-nonaccessible) (render-fields "Private instance fields" non-static-nonaccessible)
654+
(seq static-nonaccessible) (render-fields "Private static fields" static-nonaccessible)
655+
true (render-datafy obj)))))
655656

656657
(defn- render-section [obj inspector [section sort-key-fn]]
657658
(let [method (symbol (str ".get" (name section)))

0 commit comments

Comments
 (0)