special form | since v0.0-927 | clojure.core/try | Edit |
(try expr* catch-clause* finally-clause?)
The expressions (expr*
) are evaluated and, if no exceptions occur, the value
of the last is returned.
If an exception occurs and catch clauses (catch-clause*
) are provided, each is
examined in turn and the first for which the thrown exception is an instance of
the named class is considered a matching catch clause. If there is a matching
catch clause, its expressions are evaluated in a context in which name is bound
to the thrown exception, and the value of the last is the return value of the
function.
If there is no matching catch clause, the exception propagates out of the
function. Before returning, normally or abnormally, any finally-clause?
expressions will be evaluated for their side effects.
try
is one of ClojureScript's special forms.
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))))