Olov Lassus

Seems like restrict mode found us a jQuery bug

16 Jun 2011

Feel free to skip the first paragraph if you’ve read my previous restrict mode articles.

I’ve created this thing called restrict mode for JavaScript. It’s a definition of a proper subset of the language which I believe is great because I think it increases robustness and makes your program more easy to reason about. It comes with a tool that will check if your program really complies to restrict mode, when you say it does. The tool is called restricter and is a plugin for JSShaper, an extensible framework for JavaScript syntax tree shaping. Restrict mode makes JS programming more enjoyable since it helps you focus on real programming challenges instead of annoyances/defects/inconsistencies of the JS language.

I’ve written about how I made v8bench and JSLint restrict mode clean and I promised to do the same for jQuery.

I’ve tweaked the restrict mode semantics somewhat since then:

Restrict mode is still a proper subset of the JavaScript language and will remain so. It will freeze one day but we’re not there yet.

Back to jQuery. I cloned the jQuery repo, ran it through restricter (with restrict mode forced no matter "use restrict"; directives) and fixed the restrict mode run-time errors until all tests passed again. restricter ran on everything under src/ excluding the Sizzle dependency, to be precise. It should be easy to try it on Sizzle as well, or on something else for that matter.

The required changes to get jQuery restrict mode clean were few and small. They can be partitioned into three classes:

  1. Most were about fixing the str + nostr_nonum antipattern

  2. One fixing str < num

  3. One fixing str + undefined

Neither class 1 or 2 are bugs in jQuery. They represent the current mismatch between the restrict mode subset of JavaScript and the subset that the jQuery developers use. The mismatch seems to be very small, further strengthening the case for restrict mode. I only had to change a total of 9 lines of code, all by explicitly converting a primitive or object into a string using String(expr). The patched version may be a bit easier to reason about and slightly more robust to future changes, especially so for newcomers to the jQuery codebase. Being explicit makes it slightly more verbose.

The class 3 change is more interesting. str + undefined is rarely done on purpose. jQuery.fn.hasClass should guard against missing className properties but doesn’t, and as a consequence element.hasClass("undefined") may return true when it shouldn’t. An easy way to demonstrate this is to change test/unit/attributes.js, line 860. Change ok(!j.hasClass("asdf"), ...) to ok(!j.hasClass("undefined"), ...) and watch the assert blow. Talking about assert feel free to check out C-style assertions in JavaScript via JSShaper.

Seems like restrict mode found us a jQuery bug.

Here’s the patch aggregating the changes above including the bugfix. It applies cleanly against d59b0f3e27827d189b8b2595142ec6bbc3941dd9. And here’s the jQuery forum thread, ticket and pull request.

Want to try restrict mode on your own project? Go to restrictmode.org and feel free to stop by the JSShaper mailing list.

Show comments (reddit)

Follow me on Twitter

« I made JSLint restrict mode clean. Here's what I found    ↑ Home     »