 (defmacro satisfies?
   "Returns true if x satisfies the protocol"
   [psym x]
-  (let [p          (resolved-name &env psym)
-        prefix     (protocol-prefix p)
-        xsym       (bool-expr (gensym))
-        [part bit] (fast-path-protocols p)
-        msym       (symbol
-                    (core/str "-cljs$lang$protocol_mask$partition" part "$"))]
+  (let [p          (:name
+                     (cljs.analyzer/resolve-var
+                       (dissoc &env :locals) psym))
+         prefix     (protocol-prefix p)
+         xsym       (bool-expr (gensym))
+         [part bit] (fast-path-protocols p)
+         msym       (symbol
+                      (core/str "-cljs$lang$protocol_mask$partition" part "$"))]
     `(let [~xsym ~x]
        (if ~xsym
          (let [bit# ~(if bit `(unsafe-bit-and (. ~xsym ~msym) ~bit))]
            (if (or bit#
-                   ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix)))))
+                 ~(bool-expr `(. ~xsym ~(symbol (core/str "-" prefix)))))
              true
              (if (coercive-not (. ~xsym ~msym))
                (cljs.core/native-satisfies? ~psym ~xsym)

    (if (= p 'Object)
      (add-obj-methods type type-sym sigs)
      (concat
       (when-not (skip-flag psym)
         [`(set! ~(extend-prefix type-sym pprefix) true)])
       (mapcat
        (fn [sig]
          (if (= psym `IFn)
            (add-ifn-methods type type-sym sig)
            (add-proto-methods* pprefix type type-sym sig)))
        sigs)))))

(defmacro extend-type [type-sym & impls]
  (let [resolve  (partial resolved-name &env)
        impl-map (->impl-map impls)
        [type assign-impls] (if-let [type (base-type type-sym)]
                              [type base-assign-impls]
                              [(resolve type-sym) proto-assign-impls])]
    `(do ~@(mapcat #(assign-impls &env resolve type-sym type %) impl-map))))

(defn- prepare-protocol-masks [env impls]
  (let [resolve  (partial resolved-name env)
        impl-map (->impl-map impls)
        fpp-pbs  (seq
                  (keep fast-path-protocols
                        (map resolve
                             (keys impl-map))))]
    (if fpp-pbs
      (let [fpps  (into #{}
                        (filter (partial contains? fast-path-protocols)
                                (map resolve (keys impl-map))))
            parts (as-> (group-by first fpp-pbs) parts
                       (into {}
                             (map (juxt key (comp (partial map peek) val))
                                  parts))
                       (into {}
                             (map (juxt key (comp (partial reduce core/bit-or) val))
                                  parts)))]
        [fpps (reduce (fn [ps p] (update-in ps [p] (fnil identity 0)))
                      parts
                      (range fast-path-protocol-partitions-count))]))))

(defn annotate-specs [annots v [f sigs]]
  (conj v
        (vary-meta (cons f (map #(cons (second %) (nnext %)) sigs))
                   merge annots)))

(defn dt->et
  ([type specs fields]
     (let [annots {:analyzer/type          type
                   :analyzer/fields        fields
                   :protocol-impl true}]
       (loop [ret [] specs specs]
         (if (seq specs)
           (let [ret (-> (conj ret (first specs))
                       (into (reduce (partial annotate-specs annots) []
                                     (group-by first (take-while seq? (next specs))))))
                 specs (drop-while seq? (next specs))]
             (recur ret specs))
           ret)))))

(defn collect-protocols [impls env]
  (->> impls
       (filter core/symbol?)
       (map (partial resolved-name env))
       (into #{})))

(defn- build-positional-factory
  [rsym rname fields]
  (let [fn-name (symbol (core/str '-> rsym))]
    `(defn ~fn-name
       [~@fields]
       (new ~rname ~@fields))))

(defmacro deftype [t fields & impls]
  (let [r (resolved-name &env t)
        [fpps pmasks] (prepare-protocol-masks &env impls)
        protocols (collect-protocols impls &env)
        t (vary-meta t merge
                     {:protocols          protocols
                      :skip-protocol-flag fpps}) ]
    `(do
       (~'deftype* ~t ~fields ~pmasks
         ~(if (seq impls)
            `(extend-type ~t ~@(dt->et t impls fields))))
       (set! (.-cljs$lang$type ~t) true)
       (set! (.-cljs$lang$ctorStr ~t) ~(core/str r))
       (set! (.-cljs$lang$ctorPrWriter ~t) (fn [this# writer# opt#] (-write writer# ~(core/str r))))
       ~(build-positional-factory t r fields)
       ~t)))


  (when (seq (apply disj (apply core/hash-set (keys options)) valid-keys))
    (throw
     (apply core/str "Only these options are valid: "
	    (first valid-keys)
	    (map #(core/str ", " %) (rest valid-keys))))))

(defmacro defmulti
  "Creates a new multimethod with the associated dispatch function.
