 
   OpSeq is (Seq (Σ Op Opseq))"
   dispatch-fn)
+
+(defn label []
+  (keyword (gensym "label__")))
+
+(defmethod -emit :fn-method
+  [{:keys [params tag fixed-arity variadic? body env]}
+   {:keys [class] :as frame}]
+  (let [arg-tags               (mapv (comp prim-or-obj :tag) params)
+        return-type            (prim-or-obj tag)
+        tags                   (conj arg-tags return-type)
+        prim-interface         (j.u/prim-interface tags)
+
+        primitive?             (some primitive? tags)
+
+        method-name            (cond
+                                variadic? :doInvoke
+                                primitive? :invokePrim
+                                :else
+                                :invoke)
+
+        ;; arg-types
+        [loop-label end-label] (repeatedly label)
+
+        code
+        `[[:start-method]
+          [:local-variable :this :clojure.lang.AFunction nil ~loop-label ~end-label :this]
+          ~@(mapcat (fn [{:keys [name arg-id o-tag tag]}]
+                      `[~[:local-variable name tag nil loop-label end-label name]
+                        ~@(when-not (= tag o-tag)
+                            [[:load-arg arg-id]
+                             [:check-cast tag]
+                             [:store-arg arg-id]])])
+                  params)
+          [:mark ~loop-label]
+          ~@(emit-line-number env loop-label)
+          ~@(emit body (assoc frame
+                         :loop-label  loop-label
+                         :loop-locals params))
+          [:mark ~end-label]
+          [:return-value]
+          [:end-method]]]
+
+    ;; should emit typed only when there's an interface, otherwise it's useless
+
+    `[{:op     :method
+       :attr   #{:public}
+       :method [(into [method-name] arg-tags) return-type]
+       :code   code}
+      (when primitive?
+        {:op        :method
+         :attr      #{:public}
+         :interface prim-interface
+         :method    [(into [:invoke] (repeat (count params) :java.lang.Object))
+                     :java.lang.Object]
+         :code      `[[:start-method]
+                      [:load-this]
+                      ~@(mapcat (fn [{:keys [tag]} id]
+                                  `[~[:load-arg id]
+                                    ~@(emit-cast Object tag)])
+                                params (range))
+                      ~[:invoke-virtual (into [(keyword class "invokePrim")] arg-tags) return-type]
+                      ~@(emit-cast return-type Object)
+                      [:return-value]
+                      [:end-method]]})]))
+
+(defmethod -emit :def
+  [{:keys [init var]}]
+  (-> (-emit init)
+      (assoc :class-name (var->class var))))
+
+(defn emit-fn-class
+  [{:keys [class-name methods variadic? constants closed-overs env
+           annotations super interfaces op fields class-id]
+    :as ast}]
+  (let [constants          (->> constants
+                                (remove #(let [{:keys [tag type]} (val %)]
+                                           (or (primitive? tag)
+                                               (#{:string :bool} type))))
+                                (into {}))
+
+        consts             (vals constants)
+
+        constant-table     (zipmap (mapv :id consts) consts)
+
+        consts             (mapv (fn [{:keys [id tag]}]
+                                   {:op   :field
+                                    :attr #{:public :final :static}
+                                    :name (str "const__" id)
+                                    :tag  tag})
+                                 consts)
+
+        ctor-types         (into (if meta [:clojure.lang.IPersistentMap] [])
+                                 (mapv :tag closed-overs))
+
+        class-ctors        [{:op     :method
+                             :attr   #{:public :static}
+                             :method [[:<clinit>] :void]
+                             :code   `[[:start-method]
+                                       [:return-value]
+                                       [:end-method]]}
+                            {:op     :method
+                             :attr   #{:public}
+                             :method `[[:<init> ~@ctor-types] :void]
+                             :code   `[[:start-method]
+                                       [:load-this]
+                                       [:invoke-constructor [~(keyword (name super) "<init>")] :void]
+                                       [:return-value]
+                                       [:end-method]]}]
+
+        variadic-method    (when variadic?
+                             (let [required-arity (->> methods (filter :variadic?) first :fixed-arity)]
+                               [{:op     :method
+                                 :attr   #{:public}
+                                 :method [[:getRequiredArity] :int]
+                                 :code   [[:start-method]
+                                          [:push (int required-arity)]
+                                          [:return-value]
+                                          [:end-method]]}]))]
+
+    {:op          :class
+     :attr        #{:public :super :final}
+     :annotations annotations
+     :class-name  class-name
+     :name        (s/replace class-name \. \/)
+     :super       (s/replace (name super) \. \/)
+     :interfaces  interfaces
+     :fields      consts
+     :methods     (concat class-ctors
+                          variadic-method
+                          (mapcat #(-emit %) methods))}))
+
+(defmethod -emit :fn
+  [{:keys [form internal-name variadic?] :as ast}]
+  (let [class-name (str (namespace-munge *ns*) "$" (munge internal-name))
+        super      (if variadic? :clojure.lang.RestFn :clojure.lang.AFunction)
+        ast        (assoc ast
+                     :class-name class-name
+                     :super super)]
+    (emit-fn-class ast)))
 
 (defn emit-class
   "(λ Def-AST) → Class-AST

            [oxcart.passes.defs
             :refer [locate-var-as-value]]
            [clojure.tools.emitter.jvm.transform
             :refer [-compile]]))

(defn var->class [x]
  {:pre  [(var? x)]

                           whole-ast->forms
                           (keep #(and (pattern/def? %)
                                       (reach (pattern/def->var %)))))]

    ;; compile & write the set of used classes
    (doseq [ast reach-defs]
      (let [{:keys [class-name] :as class-ast} (emit-class ast emit)
            class-bc                           (-compile class-ast)]
        (write-class class-name class-bc)))

    ;; compile & write the bootstrap class
    (let [class-name (var->ns entry)
          class-ast  {}
          class-bc   (-> class-ast -compile)]
