|
8 | 8 |
|
9 | 9 | (ns cljs.repl
|
10 | 10 | (:require-macros cljs.repl)
|
11 |
| - (:require [cljs.spec.alpha :as spec])) |
| 11 | + (:require [cljs.spec.alpha :as spec] |
| 12 | + [goog.string :as gstring] |
| 13 | + [goog.string.format])) |
12 | 14 |
|
13 | 15 | (defn print-doc [{n :ns nm :name :as m}]
|
14 | 16 | (println "-------------------------")
|
|
56 | 58 | (doseq [role [:args :ret :fn]]
|
57 | 59 | (when-let [spec (get fnspec role)]
|
58 | 60 | (print (str "\n " (name role) ":") (spec/describe spec)))))))))
|
| 61 | + |
| 62 | +(defn Error->map |
| 63 | + "Constructs a data representation for a Error with keys: |
| 64 | + :cause - root cause message |
| 65 | + :phase - error phase |
| 66 | + :via - cause chain, with cause keys: |
| 67 | + :type - exception class symbol |
| 68 | + :message - exception message |
| 69 | + :data - ex-data |
| 70 | + :at - top stack element |
| 71 | + :trace - root cause stack elements" |
| 72 | + [o] |
| 73 | + (let [base (fn [t] |
| 74 | + (merge {:type (cond |
| 75 | + (instance? ExceptionInfo t) 'ExceptionInfo |
| 76 | + (instance? js/EvalError t) 'js/EvalError |
| 77 | + (instance? js/RangeError t) 'js/RangeError |
| 78 | + (instance? js/ReferenceError t) 'js/ReferenceError |
| 79 | + (instance? js/SyntaxError t) 'js/SyntaxError |
| 80 | + (instance? js/URIError t) 'js/URIError |
| 81 | + (instance? js/Error t) 'js/Error |
| 82 | + :else nil)} |
| 83 | + (when-let [msg (ex-message t)] |
| 84 | + {:message msg}) |
| 85 | + (when-let [ed (ex-data t)] |
| 86 | + {:data ed}) |
| 87 | + #_(let [st (extract-canonical-stacktrace t)] |
| 88 | + (when (pos? (count st)) |
| 89 | + {:at st})))) |
| 90 | + via (loop [via [], t o] |
| 91 | + (if t |
| 92 | + (recur (conj via t) (ex-cause t)) |
| 93 | + via)) |
| 94 | + root (peek via)] |
| 95 | + (merge {:via (vec (map base via)) |
| 96 | + :trace nil #_(extract-canonical-stacktrace (or root o))} |
| 97 | + (when-let [root-msg (ex-message root)] |
| 98 | + {:cause root-msg}) |
| 99 | + (when-let [data (ex-data root)] |
| 100 | + {:data data}) |
| 101 | + (when-let [phase (-> o ex-data :clojure.error/phase)] |
| 102 | + {:phase phase})))) |
| 103 | + |
| 104 | +(defn ex-triage |
| 105 | + "Returns an analysis of the phase, error, cause, and location of an error that occurred |
| 106 | + based on Throwable data, as returned by Throwable->map. All attributes other than phase |
| 107 | + are optional: |
| 108 | + :clojure.error/phase - keyword phase indicator, one of: |
| 109 | + :read-source :compile-syntax-check :compilation :macro-syntax-check :macroexpansion |
| 110 | + :execution :read-eval-result :print-eval-result |
| 111 | + :clojure.error/source - file name (no path) |
| 112 | + :clojure.error/line - integer line number |
| 113 | + :clojure.error/column - integer column number |
| 114 | + :clojure.error/symbol - symbol being expanded/compiled/invoked |
| 115 | + :clojure.error/class - cause exception class symbol |
| 116 | + :clojure.error/cause - cause exception message |
| 117 | + :clojure.error/spec - explain-data for spec error" |
| 118 | + [datafied-throwable] |
| 119 | + (let [{:keys [via trace phase] :or {phase :execution}} datafied-throwable |
| 120 | + {:keys [type message data]} (last via) |
| 121 | + {::spec/keys [:problems :fn :cljs.spec.test.alpha/caller]} data |
| 122 | + {:keys [:clojure.error/source] :as top-data} (:data (first via))] |
| 123 | + (assoc |
| 124 | + (case phase |
| 125 | + :read-source |
| 126 | + (let [{:keys [:clojure.error/line :clojure.error/column]} data] |
| 127 | + (cond-> (merge (-> via second :data) top-data) |
| 128 | + source (assoc :clojure.error/source source) |
| 129 | + (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} source) (dissoc :clojure.error/source) |
| 130 | + message (assoc :clojure.error/cause message))) |
| 131 | + |
| 132 | + (:compile-syntax-check :compilation :macro-syntax-check :macroexpansion) |
| 133 | + (cond-> top-data |
| 134 | + source (assoc :clojure.error/source source) |
| 135 | + (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} source) (dissoc :clojure.error/source) |
| 136 | + type (assoc :clojure.error/class type) |
| 137 | + message (assoc :clojure.error/cause message) |
| 138 | + problems (assoc :clojure.error/spec data)) |
| 139 | + |
| 140 | + (:read-eval-result :print-eval-result) |
| 141 | + (let [[source method file line] (-> trace first)] |
| 142 | + (cond-> top-data |
| 143 | + line (assoc :clojure.error/line line) |
| 144 | + file (assoc :clojure.error/source file) |
| 145 | + (and source method) (assoc :clojure.error/symbol (vector #_java-loc->source source method)) |
| 146 | + type (assoc :clojure.error/class type) |
| 147 | + message (assoc :clojure.error/cause message))) |
| 148 | + |
| 149 | + :execution |
| 150 | + (let [[source method file line] (->> trace #_(drop-while #(core-class? (name (first %)))) first) |
| 151 | + file (first (remove #(or (nil? %) (#{"NO_SOURCE_FILE" "NO_SOURCE_PATH"} %)) [(:file caller) file])) |
| 152 | + err-line (or (:line caller) line)] |
| 153 | + (cond-> {:clojure.error/class type} |
| 154 | + err-line (assoc :clojure.error/line err-line) |
| 155 | + message (assoc :clojure.error/cause message) |
| 156 | + (or fn (and source method)) (assoc :clojure.error/symbol (or fn (vector #_java-loc->source source method))) |
| 157 | + file (assoc :clojure.error/source file) |
| 158 | + problems (assoc :clojure.error/spec data)))) |
| 159 | + :clojure.error/phase phase))) |
| 160 | + |
| 161 | +(defn ex-str |
| 162 | + "Returns a string from exception data, as produced by ex-triage. |
| 163 | + The first line summarizes the exception phase and location. |
| 164 | + The subsequent lines describe the cause." |
| 165 | + [{:clojure.error/keys [phase source line column symbol class cause spec] :as triage-data}] |
| 166 | + (let [loc (str (or source "<cljs repl>") ":" (or line 1) (if column (str ":" column) "")) |
| 167 | + class-name (name (or class "")) |
| 168 | + simple-class class-name |
| 169 | + cause-type (if (contains? #{"Exception" "RuntimeException"} simple-class) |
| 170 | + "" ;; omit, not useful |
| 171 | + (str " (" simple-class ")")) |
| 172 | + format gstring/format] |
| 173 | + (case phase |
| 174 | + :read-source |
| 175 | + (format "Syntax error reading source at (%s).\n%s\n" loc cause) |
| 176 | + |
| 177 | + :macro-syntax-check |
| 178 | + (format "Syntax error macroexpanding %sat (%s).\n%s" |
| 179 | + (if symbol (str symbol " ") "") |
| 180 | + loc |
| 181 | + (if spec |
| 182 | + (with-out-str |
| 183 | + (spec/explain-out |
| 184 | + (if true #_(= s/*explain-out* s/explain-printer) |
| 185 | + (update spec ::spec/problems |
| 186 | + (fn [probs] (map #(dissoc % :in) probs))) |
| 187 | + spec))) |
| 188 | + (format "%s\n" cause))) |
| 189 | + |
| 190 | + :macroexpansion |
| 191 | + (format "Unexpected error%s macroexpanding %sat (%s).\n%s\n" |
| 192 | + cause-type |
| 193 | + (if symbol (str symbol " ") "") |
| 194 | + loc |
| 195 | + cause) |
| 196 | + |
| 197 | + :compile-syntax-check |
| 198 | + (format "Syntax error%s compiling %sat (%s).\n%s\n" |
| 199 | + cause-type |
| 200 | + (if symbol (str symbol " ") "") |
| 201 | + loc |
| 202 | + cause) |
| 203 | + |
| 204 | + :compilation |
| 205 | + (format "Unexpected error%s compiling %sat (%s).\n%s\n" |
| 206 | + cause-type |
| 207 | + (if symbol (str symbol " ") "") |
| 208 | + loc |
| 209 | + cause) |
| 210 | + |
| 211 | + :read-eval-result |
| 212 | + (format "Error reading eval result%s at %s (%s).\n%s\n" cause-type symbol loc cause) |
| 213 | + |
| 214 | + :print-eval-result |
| 215 | + (format "Error printing return value%s at %s (%s).\n%s\n" cause-type symbol loc cause) |
| 216 | + |
| 217 | + :execution |
| 218 | + (if spec |
| 219 | + (format "Execution error - invalid arguments to %s at (%s).\n%s" |
| 220 | + symbol |
| 221 | + loc |
| 222 | + (with-out-str |
| 223 | + (spec/explain-out |
| 224 | + (if true #_(= s/*explain-out* s/explain-printer) |
| 225 | + (update spec ::spec/problems |
| 226 | + (fn [probs] (map #(dissoc % :in) probs))) |
| 227 | + spec)))) |
| 228 | + (format "Execution error%s at %s(%s).\n%s\n" |
| 229 | + cause-type |
| 230 | + (if symbol (str symbol " ") "") |
| 231 | + loc |
| 232 | + cause))))) |
| 233 | + |
| 234 | +(defn error->str [error] |
| 235 | + (ex-str (ex-triage (Error->map error)))) |
0 commit comments