Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Method code too large!" when compiling the hiccup2.core/html macro #205

Closed
luontola opened this issue Nov 28, 2023 · 1 comment · Fixed by #206
Closed

"Method code too large!" when compiling the hiccup2.core/html macro #205

luontola opened this issue Nov 28, 2023 · 1 comment · Fixed by #206

Comments

@luontola
Copy link
Contributor

luontola commented Nov 28, 2023

Problem

Below is an example of a pretty small function layout-page that can't be compiled on Hiccup 2.0.0-RC2.

The code generated by the hiccup2.core/html macro exceeds Java's 64KB maximum size for a method (calculated in bytecode instructions). Trying to load this namespace will throw Method code too large! from ASM when the Clojure compiler tries to compile the method:

Syntax error compiling fn* at (example.clj:11:1).
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7132)
	at clojure.lang.Compiler.analyze(Compiler.java:6806)
        [...]
Caused by: java.lang.IndexOutOfBoundsException: Method code too large!
	at clojure.asm.MethodWriter.computeMethodInfoSize(MethodWriter.java:2061)
	at clojure.asm.ClassWriter.toByteArray(ClassWriter.java:457)
	at clojure.lang.Compiler$ObjExpr.compile(Compiler.java:4680)
	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:4118)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7122)
	... 38 more
;; this example was extracted from https://github.com/luontola/tdd-mooc/blob/036e96af12de6acd8489e99a037e4f2f4bf2c9ea/src/tdd_mooc/web.clj#L159
(ns example
  (:require [clojure.pprint]
            [clojure.walk]
            [hiccup.page]
            [hiccup2.core :as h]))

(defn layout-navigation [current-path navigation-tree])

(defn layout-footer [])

(defn layout-page [{:keys [path title content navigation-tree]}]
  #_(binding [clojure.pprint/*print-right-margin* 300]
      (clojure.pprint/pprint
       (clojure.walk/macroexpand-all)))
  (h/html (hiccup.page/doctype :html5)
          [:html {:lang "en"}
           [:head
            [:meta {:charset "utf-8"}]
            [:title #_{} ; 1
             (when (and (some? title)
                        (not= "/" path))
               (str title " - "))
             "Test-Driven Development MOOC"]
            [:meta {:name "viewport", :content "width=device-width, initial-scale=1.0"}]
            [:link {:rel "stylesheet" :href "https://cdn.jsdelivr.net/npm/@openfonts/open-sans-condensed_all@1.44.2/index.min.css"}]
            [:link {:rel "stylesheet" :href "https://cdn.jsdelivr.net/npm/@fontsource/roboto-slab@5.0.17/index.min.css"}]
            [:link {:rel "stylesheet" :href "https://assets.ubuntu.com/v1/vanilla-framework-version-4.5.0.min.css"}]
            [:link {:rel "stylesheet" :href "/styles.css"}]]
           [:body
            [:div.l-docs #_{} ; 2
             (layout-navigation path navigation-tree)

             [:div#main-content.l-docs__title #_{} ; 3
              (when (= "/" path)
                [:header.banner
                 [:div "Test-Driven Development"]
                 [:div "a plunge into the TDD programming technique"]])
              [:div.p-section--shallow
               [:div.row
                [:div.col-12
                 [:h1 #_{} ; 4
                  title]]]]]

             [:main.l-docs__main
              [:div.row
               [:div.col-12 #_{} ; 5
                content]]]]

            (layout-footer)]]))

Analysis

Note in the example, that I've commented out the empty attribute maps (#_{}) that precede function calls and other runtime values. If you change even one of them back to {}, then the generated code will be slightly less than 64KB and the function can be compiled. But if all five of them are removed/commented out, as above, then the compiler crashes.

I've created a gist which shows the macroexpand-all output and how much the generated code increases every time you remove one of the {} empty attribute maps: https://gist.github.com/luontola/bb95eb7779c3d18b5486cdfe360f843c

Cause 1

This is primarily caused by hiccup.compiler/compile-element ::literal-tag which duplicates the whole element every time it can't know at compile time whether a value is an attribute map or a child element.

For example, when the (layout-navigation path navigation-tree) call is preceded by [:div.l-docs {}, then Hiccup generates this much code:

       (. G__11038 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"l-docs\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ (hiccup.compiler/render-html (layout-navigation path navigation-tree))] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"l-docs__title\" id=\"main-content\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (.
        G__11038
        append
        (let*
         [or__5581__auto__
          (if
           (= "/" path)
           (do
            (let* [G__11041 (new java.lang.StringBuilder)] (. G__11041 append (let* [or__5581__auto__ "<header class=\"banner\"><div>Test-Driven Development</div><div>a plunge into the TDD programming technique</div></header>"] (if or__5581__auto__ or__5581__auto__ ""))) (. G__11041 toString))))]
         (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"p-section--shallow\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"row\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"col-12\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<h1"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ (hiccup.compiler/render-html title)] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</h1>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<main"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"l-docs__main\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"row\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"col-12\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ (hiccup.compiler/render-html content)] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</main>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ (hiccup.compiler/render-html (layout-footer))] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</body>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</html>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 toString))

But when the (layout-navigation path navigation-tree) call is preceded by [:div.l-docs, without the {}, then Hiccup nearly doubles the amount of generated code, even though the only difference between the variants is the starting tag:

       (.
        G__11219
        append
        (let*
         [or__5581__auto__
          (let*
           [attrs11087 (layout-navigation path navigation-tree)]
           (if
            (clojure.core/map? attrs11087)
            (let*
             [G__11222 (new java.lang.StringBuilder)]
             (. G__11222 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ (hiccup.compiler/render-attr-map (clojure.core/merge {:class "l-docs"} attrs11087))] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ " class=\"l-docs__title\" id=\"main-content\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (.
              G__11222
              append
              (let*
               [or__5581__auto__
                (if
                 (= "/" path)
                 (do
                  (let*
                   [G__11223 (new java.lang.StringBuilder)]
                   (. G__11223 append (let* [or__5581__auto__ "<header class=\"banner\"><div>Test-Driven Development</div><div>a plunge into the TDD programming technique</div></header>"] (if or__5581__auto__ or__5581__auto__ "")))
                   (. G__11223 toString))))]
               (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ " class=\"p-section--shallow\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ " class=\"row\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ " class=\"col-12\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<h1"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ (hiccup.compiler/render-html title)] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</h1>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<main"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ " class=\"l-docs__main\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ " class=\"row\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ " class=\"col-12\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ (hiccup.compiler/render-html content)] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</main>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11222 toString))
            (let*
             [G__11224 (new java.lang.StringBuilder)]
             (. G__11224 append (let* [or__5581__auto__ "<div class=\"l-docs\">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ (hiccup.compiler/render-html attrs11087)] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ " class=\"l-docs__title\" id=\"main-content\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (.
              G__11224
              append
              (let*
               [or__5581__auto__
                (if
                 (= "/" path)
                 (do
                  (let*
                   [G__11225 (new java.lang.StringBuilder)]
                   (. G__11225 append (let* [or__5581__auto__ "<header class=\"banner\"><div>Test-Driven Development</div><div>a plunge into the TDD programming technique</div></header>"] (if or__5581__auto__ or__5581__auto__ "")))
                   (. G__11225 toString))))]
               (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ " class=\"p-section--shallow\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ " class=\"row\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ " class=\"col-12\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<h1"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ (hiccup.compiler/render-html title)] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</h1>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<main"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ " class=\"l-docs__main\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ " class=\"row\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "<div"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ " class=\"col-12\""] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ (hiccup.compiler/render-html content)] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</main>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
             (. G__11224 toString))))]
         (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11219 append (let* [or__5581__auto__ (hiccup.compiler/render-html (layout-footer))] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11219 append (let* [or__5581__auto__ "</body>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11219 append (let* [or__5581__auto__ "</html>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11219 toString))

I'm working on a fix for this. I have it already working, but it needs some cleanup. I'll create a PR for it soon.

Cause 2

Big part of the generated output consists of appending small string literals to a StringBuilder:

       (. G__11038 append (let* [or__5581__auto__ "</h1>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "</div>"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ "<main"] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ " class=\"l-docs__main\""] (if or__5581__auto__ or__5581__auto__ "")))
       (. G__11038 append (let* [or__5581__auto__ ">"] (if or__5581__auto__ or__5581__auto__ "")))

It's possible to change hiccup.compiler/build-string to concatenate these string literals at compile time. We can also avoid the (if x x "") for string literals.

This will result in much less bytecode instructions, making it harder to get over the 64KB method code limit. The string literals themselves are stored in the class file's constant pool, so they don't count against the method code limit. It should also be faster to concatenate a few big strings than lots of small strings.

I have a fix for this ready and will create a PR soon, together with the fix for cause 1.

@weavejester
Copy link
Owner

That sounds fine, thanks for your investigation of this. Happy to accept a PR to reduce the compiled code size.

luontola added a commit to luontola/hiccup that referenced this issue Dec 1, 2023
luontola added a commit to luontola/hiccup that referenced this issue Dec 6, 2023
It was possible for the macros to generate so much bytecode that they
would exceed Java's 64KB limit for method code size. In such situations
the Clojure compiler would fail with the message "Method code too
large!" See weavejester#205

This commit does the following optimizations:

1. Concatenate string literals at compile time, so that we can replace
   multiple StringBuilder.append() calls with just one. This reduces the
   generated code from O(n) to O(1). This also improves performance by
   10-20%, since copying one long string is faster than many short
   strings.

2. When a runtime check is needed to determine whether a value is the
   element's attribute map or its first child element, avoid duplicating
   the code for the element's content. This reduces the generated code
   from O(n^2) to O(n).

While improving the test coverage, some edge cases of generating bad
HTML were detected. This commit doesn't change the existing behavior,
but only documents it in the new tests. Fixing that behavior will be
done in future commits.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants