CoffeeKup optimization (v7)

Revision 7 of this benchmark created on


Description

Experimental optimization of CoffeeKup templates using UglifyJS for static analysis.

This test runs the following template, from the coffeekup benchmark script:

doctype 5
html lang: 'en', ->
  head ->
    meta charset: 'utf-8'
    title @title
    style '''
      body {font-family: "sans-serif"}
      section, header {display: block}
    '
''
  body ->
    section ->
      header ->
        h1 @title
      if @inspired
        p 'Create a witty example'
      else
        p 'Go meta'
      ul ->
        for user in @users
          li user.name
          li -> a href: "mailto:\#{user.email}", -> user.email

The template is rendered using the following data:

data =
  title: 'test'
  inspired: no
  users: [
    {email: 'house@gmail.com', name: 'house'}
    {email: 'cuddy@gmail.com', name: 'cuddy'}
    {email: 'wilson@gmail.com', name: 'wilson'}
  ]

Preparation HTML

<script src="https://raw.github.com/documentcloud/underscore/master/underscore-min.js"></script>
<script>
  var template = function anonymous(data) {
      var a, b, body, em, h1, head, header, html, i, ins, li, p, s, section, style, th, title, u, ul, font, tt, hr, meta;
      a = function() {
        return __ck.tag('a', arguments);
      };
      b = function() {
        return __ck.tag('b', arguments);
      };
      body = function() {
        return __ck.tag('body', arguments);
      };
      em = function() {
        return __ck.tag('em', arguments);
      };
      h1 = function() {
        return __ck.tag('h1', arguments);
      };
      head = function() {
        return __ck.tag('head', arguments);
      };
      header = function() {
        return __ck.tag('header', arguments);
      };
      html = function() {
        return __ck.tag('html', arguments);
      };
      i = function() {
        return __ck.tag('i', arguments);
      };
      ins = function() {
        return __ck.tag('ins', arguments);
      };
      li = function() {
        return __ck.tag('li', arguments);
      };
      p = function() {
        return __ck.tag('p', arguments);
      };
      s = function() {
        return __ck.tag('s', arguments);
      };
      section = function() {
        return __ck.tag('section', arguments);
      };
      style = function() {
        return __ck.tag('style', arguments);
      };
      th = function() {
        return __ck.tag('th', arguments);
      };
      title = function() {
        return __ck.tag('title', arguments);
      };
      u = function() {
        return __ck.tag('u', arguments);
      };
      ul = function() {
        return __ck.tag('ul', arguments);
      };
      font = function() {
        return __ck.tag('font', arguments);
      };
      tt = function() {
        return __ck.tag('tt', arguments);
      };
      hr = function() {
        return __ck.tag('hr', arguments);
      };
      meta = function() {
        return __ck.tag('meta', arguments);
      };
      var __slice = Array.prototype.slice;
      var __hasProp = Object.prototype.hasOwnProperty;
      var __bind = function(fn, me) {
          return function() {
            return fn.apply(me, arguments);
          };
          };
      var __extends = function(child, parent) {
          for (var key in parent) {
            if (__hasProp.call(parent, key)) child[key] = parent[key];
          }
          function ctor() {
            this.constructor = child;
          }
          ctor.prototype = parent.prototype;
          child.prototype = new ctor;
          child.__super__ = parent.prototype;
          return child;
          };
      var __indexOf = Array.prototype.indexOf ||
      function(item) {
        for (var i = 0, l = this.length; i < l; i++) {
          if (this[i] === item) return i;
        }
        return -1;
      };
      var coffeescript, comment, doctype, h, ie, tag, text, yield, __ck, _ref, _ref2;
      if (data == null) {
        data = {};
      }
      if ((_ref = data.format) == null) {
        data.format = false;
      }
      if ((_ref2 = data.autoescape) == null) {
        data.autoescape = false;
      }
      __ck = {
        buffer: [],
        esc: function(txt) {
          if (data.autoescape) {
            return h(txt);
          } else {
            return String(txt);
          }
        },
        tabs: 0,
        repeat: function(string, count) {
          return Array(count + 1).join(string);
        },
        indent: function() {
          if (data.format) {
            return text(this.repeat('  ', this.tabs));
          }
        },
        tag: function(name, args) {
          var combo, i, _i, _len;
          combo = [name];
          for (_i = 0, _len = args.length; _i < _len; _i++) {
            i = args[_i];
            combo.push(i);
          }
          return tag.apply(data, combo);
        },
        render_idclass: function(str) {
          var c, classes, i, id, _i, _j, _len, _len2, _ref3;
          classes = [];
          _ref3 = str.split('.');
          for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
            i = _ref3[_i];
            if (__indexOf.call(i, '#') >= 0) {
              id = i.replace('#', '');
            } else {
              if (i !== '') {
                classes.push(i);
              }
            }
          }
          if (id) {
            text(" id=\"" + id + "\"");
          }
          if (classes.length > 0) {
            text(" class=\"");
            for (_j = 0, _len2 = classes.length; _j < _len2; _j++) {
              c = classes[_j];
              if (c !== classes[0]) {
                text(' ');
              }
              text(c);
            }
            return text('"');
          }
        },
        render_attrs: function(obj, prefix) {
          var k, v, _results;
          if (prefix == null) {
            prefix = '';
          }
          _results = [];
          for (k in obj) {
            v = obj[k];
            if (typeof v === 'boolean' && v) {
              v = k;
            }
            if (typeof v === 'function') {
              v = "(" + v + ").call(this);";
            }
            _results.push(typeof v === 'object' && !(v instanceof Array) ? this.render_attrs(v, prefix + k + '-') : v ? text(" " + (prefix + k) + "=\"" + (this.esc(v)) + "\"") : void 0);
          }
          return _results;
        },
        render_contents: function(contents) {
          var result;
          switch (typeof contents) {
          case 'string':
          case 'number':
          case 'boolean':
            return text(this.esc(contents));
          case 'function':
            if (data.format) {
              text('\n');
            }
            this.tabs++;
            result = contents.call(data);
            if (typeof result === 'string') {
              this.indent();
              text(this.esc(result));
              if (data.format) {
                text('\n');
              }
            }
            this.tabs--;
            return this.indent();
          }
        },
        render_tag: function(name, idclass, attrs, contents) {
          this.indent();
          text("<" + name);
          if (idclass) {
            this.render_idclass(idclass);
          }
          if (attrs) {
            this.render_attrs(attrs);
          }
          if (__indexOf.call(this.self_closing, name) >= 0) {
            text(' />');
            if (data.format) {
              text('\n');
            }
          } else {
            text('>');
            this.render_contents(contents);
            text("</" + name + ">");
            if (data.format) {
              text('\n');
            }
          }
          return null;
        }
      };
      tag = function() {
        var a, args, attrs, contents, idclass, name, _i, _len;
        name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
        for (_i = 0, _len = args.length; _i < _len; _i++) {
          a = args[_i];
          switch (typeof a) {
          case 'function':
            contents = a;
            break;
          case 'object':
            attrs = a;
            break;
          case 'number':
          case 'boolean':
            contents = a;
            break;
          case 'string':
            if (args.length === 1) {
              contents = a;
            } else {
              if (a === args[0]) {
                idclass = a;
              } else {
                contents = a;
              }
            }
          }
        }
        return __ck.render_tag(name, idclass, attrs, contents);
      };
      yield = function(f) {
        var old_buffer, temp_buffer;
        temp_buffer = [];
        old_buffer = __ck.buffer;
        __ck.buffer = temp_buffer;
        f();
        __ck.buffer = old_buffer;
        return temp_buffer.join('');
      };
      h = function(txt) {
        return String(txt).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
      };
      doctype = function(type) {
        if (type == null) {
          type = 'default';
        }
        text(__ck.doctypes[type]);
        if (data.format) {
          return text('\n');
        }
      };
      text = function(txt) {
        __ck.buffer.push(String(txt));
        return null;
      };
      comment = function(cmt) {
        text("<!--" + cmt + "-->");
        if (data.format) {
          return text('\n');
        }
      };
      coffeescript = function(param) {
        switch (typeof param) {
        case 'function':
          return script("" + __ck.coffeescript_helpers + "(" + param + ").call(this);");
        case 'string':
          return script({
            type: 'text/coffeescript'
          }, function() {
            return param;
          });
        case 'object':
          param.type = 'text/coffeescript';
          return script(param);
        }
      };
      ie = function(condition, contents) {
        __ck.indent();
        text("<!--[if " + condition + "]>");
        __ck.render_contents(contents);
        text("<![endif]-->");
        if (data.format) {
          return text('\n');
        }
      };
      __ck.doctypes = {
        "5": "<!DOCTYPE html>",
        "default": "<!DOCTYPE html>",
        "xml": "<?xml version=\"1.0\" encoding=\"utf-8\" ?>",
        "transitional": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">",
        "strict": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">",
        "frameset": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">",
        "1.1": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">",
        "basic": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML Basic 1.1//EN\" \"http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd\">",
        "mobile": "<!DOCTYPE html PUBLIC \"-//WAPFORUM//DTD XHTML Mobile 1.2//EN\" \"http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd\">",
        "ce": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"ce-html-1.0-transitional.dtd\">"
      };
      __ck.coffeescript_helpers = "var __slice = Array.prototype.slice;var __hasProp = Object.prototype.hasOwnProperty;var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };var __extends = function(child, parent) {  for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }  function ctor() { this.constructor = child; }  ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype;  return child; };var __indexOf = Array.prototype.indexOf || function(item) {  for (var i = 0, l = this.length; i < l; i++) {    if (this[i] === item) return i;  } return -1; };";
      __ck.self_closing = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr", "basefont", "frame"];
      (function() {
        doctype(5);
        html({
          lang: 'en'
        }, function() {
          head(function() {
            meta({
              charset: 'utf-8'
            });
            title(this.title);
            return style('body {font-family: "sans-serif"}\nsection, header {display: block}');
          });
          return body(function() {
            return section(function() {
              header(function() {
                return h1(this.title);
              });
              if (this.inspired) {
                p('Create a witty example');
              } else {
                p('Go meta');
              }
              return ul(function() {
                var user, _i, _len, _ref, _results;
                _ref = this.users;
                _results = [];
                for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                  user = _ref[_i];
                  li(user.name);
                  _results.push(li(function() {
                    return a({
                      href: "mailto:" + user.email
                    }, function() {
                      return user.email;
                    });
                  }));
                }
                return _results;
              });
            });
          });
        });
      }).call(data);
      return __ck.buffer.join('');
      };

  var optimized = function anonymous(data) {
      __ck = {
        buffer: ''
      };
      text = function(txt) {
        if (typeof txt === 'string' || txt instanceof String) {
          __ck.buffer += txt;
        } else if (typeof txt === 'number' || txt instanceof Number) {
          __ck.buffer += String(txt);
        }
      };
      h = function(txt) {
        var escaped;
        if (typeof txt === 'string' || txt instanceof String) {
          escaped = txt.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
        } else {
          escaped = txt;
        }
        return escaped;
      };
      yield = function(f) {
        var temp_buffer = '';
        var old_buffer = __ck.buffer;
        __ck.buffer = temp_buffer;
        f();
        temp_buffer = __ck.buffer;
        __ck.buffer = old_buffer;
        return temp_buffer;
      };
      ((function() {
        text("<!DOCTYPE html>");
        text('<html lang="en">');
        text(function() {
          text("<head>");
          text(function() {
            text('<meta charset="utf-8" />');
            text("<title>");
            text(this.title);
            text("</title>");
            return function() {
              text("<style>");
              text('body {font-family: "sans-serif"}\nsection, header {display: block}');
              text("</style>");
            }.call(data);
          }.call(data));
          text("</head>");
          return function() {
            text("<body>");
            text(function() {
              return function() {
                text("<section>");
                text(function() {
                  text("<header>");
                  text(function() {
                    return function() {
                      text("<h1>");
                      text(this.title);
                      text("</h1>");
                    }.call(data);
                  }.call(data));
                  text("</header>");
                  if (this.inspired) {
                    text("<p>");
                    text("Create a witty example");
                    text("</p>");
                  } else {
                    text("<p>");
                    text("Go meta");
                    text("</p>");
                  }
                  return function() {
                    text("<ul>");
                    text(function() {
                      var user, _i, _len, _ref, _results;
                      _ref = this.users;
                      _results = [];
                      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
                        user = _ref[_i];
                        text("<li>");
                        text(user.name);
                        text("</li>");
                        _results.push(function() {
                          text("<li>");
                          text(function() {
                            return function() {
                              text('<a href="');
                              text("mailto:" + user.email);
                              text('">');
                              text(function() {
                                return user.email;
                              }.call(data));
                              text("</a>");
                            }.call(data);
                          }.call(data));
                          text("</li>");
                        }.call(data));
                      }
                      return _results;
                    }.call(data));
                    text("</ul>");
                  }.call(data);
                }.call(data));
                text("</section>");
              }.call(data);
            }.call(data));
            text("</body>");
          }.call(data);
        }.call(data));
        text("</html>");
      })).call(data);
      return __ck.buffer;
      };

  var eco_template = function(__obj) {
      if (!__obj) __obj = {};
      var __out = [],
          __capture = function(callback) {
          var out = __out,
              result;
          __out = [];
          callback.call(this);
          result = __out.join('');
          __out = out;
          return __safe(result);
          },
          __sanitize = function(value) {
          if (value && value.ecoSafe) {
            return value;
          } else if (typeof value !== 'undefined' && value != null) {
            return __escape(value);
          } else {
            return '';
          }
          },
          __safe, __objSafe = __obj.safe,
          __escape = __obj.escape;
      __safe = __obj.safe = function(value) {
        if (value && value.ecoSafe) {
          return value;
        } else {
          if (!(typeof value !== 'undefined' && value != null)) value = '';
          var result = new String(value);
          result.ecoSafe = true;
          return result;
        }
      };
      if (!__escape) {
        __escape = __obj.escape = function(value) {
          return ('' + value).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
        };
      }(function() {
        (function() {
          var user, _i, _len, _ref;
          __out.push('<!DOCTYPE html>\n<html lang="en">\n  <head>\n    <meta charset="utf-8">\n    <title>');
          __out.push(__sanitize(this.title));
          __out.push('</title>\n    <style>\n      body {font-family: "sans-serif"}\n      section, header {display: block}\n    </style>\n  </head>\n  <body>\n    <section>\n      <header>\n        <h1>');
          __out.push(__sanitize(this.title));
          __out.push('</h1>\n      </header>\n      ');
          if (this.inspired) {
            __out.push('\n        <p>Create a witty example</p>\n      ');
          } else {
            __out.push('\n        <p>Go meta</p>\n      ');
          }
          __out.push('\n      <ul>\n        ');
          _ref = this.users;
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            user = _ref[_i];
            __out.push('\n          <li>');
            __out.push(__sanitize(user.name));
            __out.push('</li>\n          <li><a href="mailto:');
            __out.push(__sanitize(user.email));
            __out.push('">');
            __out.push(__sanitize(user.email));
            __out.push('</a></li>\n        ');
          }
          __out.push('\n      </ul>\n    </section>\n  </body>\n</html>');
        }).call(this);

      }).call(__obj);
      __obj.safe = __objSafe, __obj.escape = __escape;
      return __out.join('');
      };

  var data = {
    title: 'test',
    inspired: false,
    users: [{
      email: 'house@gmail.com',
      name: 'house'
    }, {
      email: 'cuddy@gmail.com',
      name: 'cuddy'
    }, {
      email: 'wilson@gmail.com',
      name: 'wilson'
    }]
  };
</script>
<script src="http://dl.dropbox.com/u/5186182/funcd.js">
</script>
<script>
var funcd_template = function(a, b) {
 return a.doctype(5),a.html({lang:"en"},function(){return a.head(function(){return a.meta({charset:"utf-8"}),a.title(b.title)}),a.body(function(){return a.section(function(){return a.header(function(){return a.h1(b.title)}),b.inspired?a.p("Create a witty example"):a.p("Go meta"),a.ul(function(){var c,d,e,f;f=b.users;for(d=0,e=f.length;d<e;d++)c=f[d],a.li(c.name),a.li(function(){return a.a({href:"mailto:"+c.email},c.email)})})})})})

};
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
Compiled template
template(data);
ready
Optimized template
optimized(data);
ready
Eco template
eco_template(data);
ready
funcd
Funcd.render(funcd_template, data);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.