special form | since v0.0-927 | clojure.core/catch | Edit |
(catch classname name expr*)
catch
should be used inside of a try
expression.
exception-type
should be the type of exception thrown (usually js/Error
or
js/Object
). When there is a match, the thrown exception will be bound to
name
inside of expr*
and expr*
will be evaluated and returned as the value
of the try
expression.
Since JavaScript allows you to throw anything, exception-type
can be set to
:default
to catch all types of exceptions.
catch-clause => (catch classname name expr*) finally-clause => (finally expr*) Catches and handles JavaScript exceptions.
(defmethod parse 'try
[op env [_ & body :as form] name _]
(let [catchenv (update-in env [:context] #(if (= :expr %) :return %))
catch? (every-pred seq? #(= (first %) 'catch))
default? (every-pred catch? #(= (second %) :default))
finally? (every-pred seq? #(= (first %) 'finally))
{:keys [body cblocks dblock fblock]}
(loop [parser {:state :start :forms body
:body [] :cblocks [] :dblock nil :fblock nil}]
(if (seq? (:forms parser))
(let [[form & forms*] (:forms parser)
parser* (assoc parser :forms forms*)]
(case (:state parser)
:start (cond
(catch? form) (recur (assoc parser :state :catches))
(finally? form) (recur (assoc parser :state :finally))
:else (recur (update-in parser* [:body] conj form)))
:catches (cond
(default? form) (recur (assoc parser* :dblock form :state :finally))
(catch? form) (recur (update-in parser* [:cblocks] conj form))
(finally? form) (recur (assoc parser :state :finally))
:else (throw (error env "Invalid try form")))
:finally (recur (assoc parser* :fblock form :state :done))
:done (throw (error env "Unexpected form after finally"))))
parser))
finally (when (seq fblock)
(-> (disallowing-recur (analyze (assoc env :context :statement) `(do ~@(rest fblock))))
(assoc :body? true)))
e (when (or (seq cblocks) dblock) (gensym "e"))
default (if-let [[_ _ name & cb] dblock]
`(cljs.core/let [~name ~e] ~@cb)
`(throw ~e))
cblock (if (seq cblocks)
`(cljs.core/cond
~@(mapcat
(fn [[_ type name & cb]]
(when name (assert (not (namespace name)) "Can't qualify symbol in catch"))
`[(cljs.core/instance? ~type ~e)
(cljs.core/let [~name ~e] ~@cb)])
cblocks)
:else ~default)
default)
locals (:locals catchenv)
locals (if e
(assoc locals e
{:name e
:line (get-line e env)
:column (get-col e env)})
locals)
catch (when cblock
(disallowing-recur (analyze (assoc catchenv :locals locals) cblock)))
try (disallowing-recur (analyze (if (or e finally) catchenv env) `(do ~@body)))]
{:env env :op :try :form form
:body (assoc try :body? true)
:finally finally
:name e
:catch catch
:children (vec
(concat [:body]
(when catch
[:catch])
(when finally
[:finally])))}))
(defmethod emit* :try
[{try :body :keys [env catch name finally]}]
(let [context (:context env)]
(if (or name finally)
(do
(when (= :expr context)
(emits "(function (){"))
(emits "try{" try "}")
(when name
(emits "catch (" (munge name) "){" catch "}"))
(when finally
(assert (not= :const (:op (ana/unwrap-quote finally))) "finally block cannot contain constant")
(emits "finally {" finally "}"))
(when (= :expr context)
(emits "})()")))
(emits try))))