 
 (defmacro deftype [t fields & impls]
   (let [r (:name (cljs.analyzer/resolve-var (dissoc &env :locals) t))
-        [fpps pmasks] (prepare-protocol-masks &env t impls)
+        [fpps pmasks] (prepare-protocol-masks &env impls)
         protocols (collect-protocols impls &env)
         t (vary-meta t assoc
             :protocols protocols

                                    (concat [~@(map #(list `vector (keyword %) %) base-fields)]
                                            ~'__extmap))))
                  ])
          [fpps pmasks] (prepare-protocol-masks env tagname impls)
          protocols (collect-protocols impls env)
          tagname (vary-meta tagname assoc
                    :protocols protocols

(defn prototype-prefix [tsym sym]
  `(.. ~tsym -prototype ~(to-property sym)))

(defn adapt-obj-params [type [[this & args :as sig] & body]]
  (list (vec args)
    (list* 'this-as (vary-meta this assoc :tag type) body)))

(defn adapt-ifn-params [type [[this & args :as sig] & body]]
  (let [self-sym (with-meta 'self__ {:tag type})]
    `(~(vec (cons self-sym args))
       (this-as ~self-sym
         (let [~this ~self-sym]
           ~@body)))))

(defn adapt-proto-params [type [[this & args :as sig] & body]]
  `(~(vec (cons (vary-meta this assoc :tag type) args))
     (this-as ~this
       ~@body)))

(defn add-obj-methods [type type-sym sigs]
  (map (fn [[f & meths :as form]]
         `(set! ~(prototype-prefix type-sym f)
            ~(with-meta `(fn ~@(map #(adapt-obj-params type %) meths)) (meta form))))
    sigs))

(defn add-ifn-methods [type type-sym [f & meths :as form]]
  (let [meths    (map #(adapt-ifn-params type %) meths)
        this-sym (with-meta 'self__ {:tag type})
        argsym   (gensym "args")]
    [`(set! ~(prototype-prefix type-sym 'call) ~(with-meta `(fn ~@meths) (meta form)))
     `(set! ~(prototype-prefix type-sym 'apply)
        ~(with-meta
           `(fn ~[this-sym argsym]
              (this-as ~this-sym
                (.apply (.-call ~this-sym) ~this-sym
                  (.concat (array ~this-sym) (aclone ~argsym)))))
           (meta form)))]))

(defn add-proto-methods* [pprefix type type-sym [f & meths :as form]]
  (let [pf (core/str pprefix f)]
    (if (vector? (first meths))
      ;; single method case
      (let [meth meths]
        [`(set! ~(prototype-prefix type-sym (core/str pf "$arity$" (count (first meth))))
            ~(with-meta `(fn ~@(adapt-proto-params type meth)) (meta form)))])
      (map (fn [[sig & body :as meth]]
             `(set! ~(prototype-prefix type-sym (core/str pf "$arity$" (count sig)))
                ~(with-meta `(fn ~(adapt-proto-params type meth)) (meta form))))
        meths))))

(defn proto-assign-impls [env resolve type-sym type [p sigs]]
  (warn-and-update-protocol p type env)
  (let [psym      (resolve p)
        pprefix   (protocol-prefix psym)
        skip-flag (set (-> type-sym meta :skip-protocol-flag))]
    (if (= p 'Object)
      (add-obj-methods type type-sym sigs)
      (concat
        (when-not (skip-flag psym)
          [`(set! ~(prototype-prefix type-sym pprefix) true)])
        (mapcat
          (fn [sig]
            (if (= psym 'cljs.core/IFn)
              (add-ifn-methods type type-sym sig)
              (add-proto-methods* pprefix type type-sym sig)))
          sigs)))))

(defmacro extend-type [type-sym & impls]
  (let [env &env
        resolve (partial resolve-var 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 resolve-var 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]
    (dt->et type specs fields false))
  ([type specs fields inline]
    (let [annots {:cljs.analyzer/type type
                  :cljs.analyzer/fields fields
                  :protocol-impl true
                  :protocol-inline inline}]
      (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
