interop: String/valueOf static + String is a CharSequence

Two interop gaps surfaced bringing up hiccup and malli:
- String/valueOf(Object): hiccup's compiler stringifies attribute values with
  (String/valueOf (or arg "")). Added the static — "null" for nil, else core-str.
- (instance? CharSequence s) returned false for a string; String implements
  CharSequence, and malli's :re validator gates on it before matching, so :re
  schemas always failed. instance-check now answers true for strings.
This commit is contained in:
Yogthos 2026-06-15 15:15:32 -04:00
parent c22b23fb51
commit 60980df767
3 changed files with 18 additions and 0 deletions

View file

@ -707,6 +707,10 @@
"Double" (number? val) "Double" (number? val)
"String" (string? val) "String" (string? val)
"java.lang.String" (string? val) "java.lang.String" (string? val)
# String implements CharSequence — malli's :re validator gates on
# (instance? CharSequence x) before matching (jolt-ltwk).
"CharSequence" (string? val)
"java.lang.CharSequence" (string? val)
"Boolean" (or (= true val) (= false val)) "Boolean" (or (= true val) (= false val))
"Keyword" (keyword? val) "Keyword" (keyword? val)
# regex patterns (cuerdas-style (instance? Pattern x) checks) # regex patterns (cuerdas-style (instance? Pattern x) checks)

View file

@ -288,6 +288,12 @@
# (String. bytes) / (String. bytes charset): UTF-8 bytes to string. # (String. bytes) / (String. bytes charset): UTF-8 bytes to string.
(each nm ["String" "java.lang.String"] (each nm ["String" "java.lang.String"]
(register-class-ctor! nm (fn [x &opt charset] (string x)))) (register-class-ctor! nm (fn [x &opt charset] (string x))))
# String statics. valueOf(Object) yields "null" for nil, else toString —
# hiccup's compiler emits (String/valueOf (or arg "")) to stringify attribute
# values; core-str gives jolt's str semantics for keywords/numbers/etc.
(each nm ["String" "java.lang.String"]
(register-class-statics! nm
@{"valueOf" (fn [x &opt off cnt] (if (nil? x) "null" (core-str x)))}))
# java.net.URL: enough for selmer's template cache — file: URLs only. # java.net.URL: enough for selmer's template cache — file: URLs only.
# A protocol-less spec throws (selmer catches MalformedURLException and # A protocol-less spec throws (selmer catches MalformedURLException and
# prepends file:///), and getPath hands back a stat-able filesystem path. # prepends file:///), and getPath hands back a stat-able filesystem path.

View file

@ -79,6 +79,14 @@
[".charAt" "\\b" "(.charAt \"abc\" 1)"] [".charAt" "\\b" "(.charAt \"abc\" 1)"]
[".equalsIgnoreCase" "true" "(.equalsIgnoreCase \"AbC\" \"aBc\")"] [".equalsIgnoreCase" "true" "(.equalsIgnoreCase \"AbC\" \"aBc\")"]
["Long/MAX_VALUE" "true" "(pos? Long/MAX_VALUE)"] ["Long/MAX_VALUE" "true" "(pos? Long/MAX_VALUE)"]
# String/valueOf static (jolt-nkx): hiccup stringifies attribute values with it.
["String/valueOf num" "\"42\"" "(String/valueOf 42)"]
["String/valueOf str" "\"hi\"" "(String/valueOf \"hi\")"]
["String/valueOf kw" "\":k\"" "(String/valueOf :k)"]
["String/valueOf nil" "\"null\"" "(String/valueOf nil)"]
# String implements CharSequence (malli's :re gates on it, jolt-ltwk).
["instance? CharSequence" "true" "(instance? CharSequence \"aaa\")"]
["instance? CharSequence non-str" "false" "(instance? CharSequence 42)"]
["unsupported method throws" :throws "(.frobnicate \"abc\")"]) ["unsupported method throws" :throws "(.frobnicate \"abc\")"])
# java.time shims (jolt-ea7): epoch-ms backed values + a DateTimeFormatter # java.time shims (jolt-ea7): epoch-ms backed values + a DateTimeFormatter