Links

pmuellr is Patrick Mueller

other pmuellr thangs: home page, twitter, flickr, github

Friday, June 04, 2010

bind() considered harmful

A recent passive-aggressive twitter message from me:

Oh yeah. Keep on bind()'ing folks, because bind() rocks. http://bit.ly/aPyfuV

In case you can't tell, the message is facetious. bind() does not actually rock.

what is bind()?

bind() is a JavaScript function implemented by many JavaScript libraries and included in ECMAScript 5. If you aren't already familiar with bind(), this post isn't going to make much sense, but if you're curious anyway, see Prototype's documentation for their bind().

Let's look at what bind() is actually doing:

  • associates an object to use as this (aka the receiver) when the function is invoked
  • associates additional parameters to be passed to the function when the function is invoked (aka currying).

bind() does this by returning a new function, which when invoked, arranges for the object you want as the receiver and the additional parameters to be passed to the original function. It hides these values in the newly created function, associated with the closure the function was returned from.

bind() is frequently used for callbacks and event handlers, which typically allow you to pass a function as the callback, but don't provide a way to specify the receiver or additional parameters for the actual call to the callback.

why does bind() not rock?

  • gets in the way of debugging

    Take a look at the bug I linked to in the Twitter message above: Bug 40080: Web Inspector: better Function.prototype.bind for the internal code

    The bug concerns providing a better debug story around functions that have been bind()'d. Because the current story isn't very pretty. When you stumble upon a bind()'d function in the debugger, you see the source for the bind() function itself, which isn't what you want to see. In addition the resulting bound function is usually anonymous, which means your stack traces, profile reports, etc, will be filled with (anonymous function) entries.

  • garbage

    Calling bind() creates a new function object every time it's called. Not just a string, or empty object, or array. A function. Which I'm guessing is more expensive than simpler objects. [yes, I should do some measurements.] A closure is also created and associated with the function (which is where the receiver and curried arguments are stored). More garbage.

    Now imagine that, for whatever reason, you need to add and then remove a callback frequently, over and over again, for some reason. Or set a timer over and over again. And you're using bind(). Think of the garbage you're creating.

  • Alex Russell posted some thoughts on bind() in a post to the es-discuss mailing list (item 3).

    So why does this [using bind()] suck? Two reasons: it's long-ish to type, and it doesn't do what the dot operator does -- i.e., return the same function object every time.

    It is longer, and therefore yuckier. It's also often not DRY (obj referenced twice):

        setTimeout( obj.method.bind(obj, "John"), 100 );
    

    The second point is explained by Alex in his post:

    Many functions, both in the DOM and in the library, accept functions as arguments. ES5 provides a bind() method that can ease the use of these functions when the passed method comes from an object. This is, however, inconvenient. E.g.:
       node.addEventListener("click", obj.method.bind(obj, ...));
    
    Also, insufficient. Event listeners can't be detached when using Function.prototype.bind:
       // doesn't do what it looks like it should
       node.removeEventListener("click", obj.method.bind(obj, ...));
    

    So the trick here, if you want to use removeEventListener(), is that you have to store the result of a single call to bind() and then use that value in subsequent addEventListener() / removeEventListener() paired calls. Alex's post suggests a new language feature to work around this (btw, I'm not in favor of his proposed language feature).

why are we in a bind with bind() today?

It's pretty obvious to see how we got to the point where you need to use bind() in your code today.

Historically, JavaScript was a glue language that let you do a light amount of programmatic processing against stuff in your page. When specifying a callback/listener, you didn't have to worry so much about the receiver of the function you passed in; you were probably using global variables instead of creating your own little objects.

And so the places that take callbacks, like setTimeout(), onload handlers, etc, didn't really have a need for you to specify the receiver of of the callback when it was invoked. The receiver was always ... well, whatever it was for your callback.

Fast-forward a decade, and now we have people building huge systems out of JavaScript, using some sort of "class" story, or living the hippy prototype lifestyle, or who knows what kids are doing today. In any case, there's often an "object" in the picture, and you'd often like to arrange to have that object be the receiver of the callback. Quite often, you'd like for the callback function to be a method of an object, and have the receiver of the callback be that object.

The problem is, there's no where to put the receiver; all the pre-existing callback patterns just allowed the use of a function parameter. The trick with bind() is that it attaches the receiver, and possibly curry'd arguments, to an invisible bag wrapped around a newly created function which is a delegated version of your callback function. Nature will find a way.

how can we fix the evils of bind()?

For me, the root of the problem is that we're passing the method receiver in a secondary channel, the bound function. So, stop doing that. Pass it explicitly.

Let's play with changing the addEventListener() function to accommodate a new receiver parameter. Here's the current function signature:

    target.addEventListener(type, listener, useCapture)

We can add the receiver parameter to the end of the function:

    target.addEventListener(type, listener, useCapture, receiver)

or we could allow listener and receiver to be combined together in an array and used where the existing listener value is today:

    target.addEventListener(type, [receiver, listener], useCapture)

This second flavor tastes better to me.

what does the fix smell like?

Let's compare the code. For the examples below, obj is the receiver of the callback, callback() is a method available on the obj object.

Using ECMAScript 5's bind() method:

    node.addEventListener("click", obj.callback.bind(obj))

Here's the four-arg addEventListener():

    node.addEventListener("click", obj.callback, false, obj)

Here's the two-element-array-listener addEventListener():

    node.addEventListener("click", [obj, obj.callback])

These examples could be made even DRYer, if instead of passing a function reference, you pass a string, which will be used as a property name to obtain the function from the receiver object:

    node.addEventListener("click", "callback", false, obj)
    node.addEventListener("click", [obj, "callback"])

In terms of being able to handle the removeEventListener() case as well, when using the ECMAScript 5 version of bind() you would have to arrange to store a copy of the bound function, so you can send the exact same function both addEventListener() and removeEventListener(). My proposed versions could do a compare against the parameters or array elements, allowing you to use the exact same parameters on addEventListener() and removeEventListener().

In other words, here's how you do it in ECMAScript 5:

    var boundFunction = obj.callback.bind(obj)
    
    node.addEventListener("click", boundFunction)
    ...
    node.removeEventListener("click", boundFunction)

And here's how you do it with my proposal:

    node.addEventListener("click", [obj, "callback"])
    ...
    node.removeEventListener("click", [obj, "callback"])

This invocation pattern works the same for the other form of call that I proposed.

Curried arguments can be handled the same way the receiver parameter is handled; passed as additional arguments to addEventListener() (not needed for removeEventListener()?), or an additional element in the array where the listener argument was previously used. One simplification would be to allow a single curried argument - other callback systems typically refer to this as userData or clientData - rather than deal with a variadic list. It's simple enough to combine multiple curried arguments into an object or array for use as a userData argument.

actually, bind() isn't always evil

Although I've spent this entire post complaining about bind(), I will acknowledge it's power and usefulness. Particularly in meta-programming and function programming.

My primary complaint is having to use bind() is something pedestrian as callbacks. That's too much.

the obligatory Smalltalk reference

My views on this subject are biased by my exposure to OTI/IBM Smalltalk. Read up on the Callbacks section on page 150 of "IBM Smalltalk: Programmer's Reference".

You'll note that my suggestion here is no different than the addCallback:receiver:selector:clientData: method described in that manual, just a shorter name.

Wednesday, June 02, 2010

coopitition

A neat thing happened last month. I entered a feature request against the Web Inspector debugger shipped with WebKit, to get some new function added. Two weeks later, that function was implemented in the FireBug debugger for FireFox. Coincidence?

The feature request was this one: Bug 38664: Web Inspector: add a "table" method to console, to allow output of tabular data. I got the idea for this from looking at another Web Inspector bug: Bug 30897: Web Inspector: Support Debugging via HTTP Headers like FirePHP and FireLogger. Turns out that FirePHP (another interesting project) has had support for tabular console output for a while. I immediately felt left out.

So how did the FireBug folks beat the Web Inspector folks (me included) to the implementation? Turns out that IBM employs folks who work on both Web Inspector and FireBug. My primary contact for FireBug stuff in IBM is John J Barton, and I thought this new function would be interesting enough they'd want it too. I guess I was right. John and I trade notes on stuff like this from time to time.

Once Jan Odvarko (aka "Honza") posted his blog entry Tabular logs in Firebug, my interest in this bug grew, for some reason :-) So, had some conversation with Jan, on the bug, last Friday. It was great for Jan to provide additional feedback in WebKit's Bugzilla. And then I was totally surprised yesterday morning to find that a bunch of additional great ideas around the feature request got posted back to the bug over the weekend, from Jan and other Web Inspector developers.

I decided to try to summarize where we were, provide some examples, and ended up writing a little console.table() simulator to play with, all of which is currently available in this file: console.table() proposal. Please post comments on that file back to the bug, for now.

I love friendly competition!

Wednesday, April 28, 2010

the weinre experiment - WEb INspector REmote

For the last few months, one of the things I've been working on is an experiment called weinre, which stands for WEb INspector REmote. The basic ideas are:

  • reuse the existing Web Inspector UI code, implemented in HTML, CSS, and JS
  • get that Web Inspector code to run as a plain old web page
  • have that Web Inspector code debug a web page running in another browser window, perhaps on another machine

You might ask - "Why?" The answer is, largely: mobile. How am I supposed to debug mobile web apps on an iPhone, Android device, or similar? Today the answer is: painfully. Tomorrow the answer should be: via a nice debug user interface like Web Inspector.

The source and an executable demo are available on WebKit's wiki, currently at the bottom of the Web Inspector page:

I have an introductory write-up of the experiment at my web site, and it's also included with the source and executable demo:

If you are truly lazy and don't want to bother with those, but can afford 3 minutes for a demo movie, you can watch it on YouTube. Best viewed in 720p (HD) in full screen mode, if your browser supports that.

Let me emphasize that this is an experiment. Although parts of it do work, as shown in the movie, it's a bit unwieldy and insecure to use as a production tool. I was at a good place to stop, and so thought I'd archive the wad up for other people to experiment with.

So, what's next?

  • Security, as noted in the write-up, is a big concern of mine. Must do something there.

  • Remove the requirement for the separate agent/server. Meaning that one of the debug client or the debug target should act as a server to it's peer. Or perhaps both debug client and debug target should be able to act as the server. Or use some other whacky IPC mechanism.

  • Formalize some structure around the messages sent between the target and client. Today the messages sent between the debug target and debug client are very specific to the Web Inspector implementation. It would be nice to have something less specific. One of them things called an A-P-I.

  • Not sure where my time is going to be spent over the next year, but it might not be here, so the answer to "What's next?" might be: "nothing, from me". But the tea leaves seem to be reading: "remote web debug".

Friday, February 26, 2010

prior art

A few caveats before starting:

  • I am not a lawyer, just an old country programmer, currently working at IBM.
  • This blog post is my own and does not necessarily represent IBM’s positions, strategies or opinions.
  • The patent infringement suit I mention below does not involve IBM as a litigant. It does involve me as a fact witness.
  • No need to go into details on the patent infringement suit, as they are irrelevant to this blog post.

Tim Bray recently wrote a blog post, "Giving Up On Patents", providing his opinion on the state of patents and linking to a number of interesting patent-related texts on the web. My thoughts are currently roughly aligned with Tim's, so there's no need to restate them.

Since I was recently involved as a fact witness in a patent infringement suit, I thought I'd provide some tips if the following are true for you:

  • You believe that there are a lot of patents granted for which there already exists prior art, and so those patents really shouldn't be granted in the first place.

  • You make things.

The tips:

  • Document the crap out of the things that you make.

  • Put your documentation on the web, and make sure search engines can find it 20 years from now.

For the suit I was involved with, an attorney of one of the defendants did a Google search on a term relevant to the suit, and found a paper I had written 13 years ago, on my personal web site. The paper they found lead them to believe I had information relevant to the suit; they thought I had evidence of prior art. Eventually, a lot more documentation was excavated from various people and places.

I found it interesting that a couple of movies were added to the pile of evidence I was asked about during my deposition, all of which appeared to be digitized versions of tape. They were all also around 13 years old. I suspect video will become even more popular as evidence in the future, given the ease in creating and disseminating it, and the amount of information it provides. Lawyers love information. Think screencasts, or perhaps ASCIIcasts are more relevant for the thing you made.

In case you're morbidly curious, I don't believe the statements I provided during my deposition actually helped the defendant in any way. Based on the questions I was asked, I have my guesses as to whether I helped anyone, but I have no way of knowing. I didn't study the patent in question, so I don't know what claims the plaintiff is making, nor do I know how the defense is planning to defend against those claims. All that is irrelevant for a fact witness; I was there to objectively answer questions that were asked of me. Just the facts, ma'am.

A couple of slightly humorous notes:

  • During the deposition, the court's Windows machine could not be coaxed to play the videos (missing codecs). One of the younger attorneys saved the day with his MacBook.

  • Making some small talk (heh) with my counsel during a break, I mentioned that I might claim "software patents are against my religion" if I was ever asked to do something I really didn't want to do. He had a good laugh, and said he'd never heard that before. Note taken.

  • The paper that the attorney found on the web had a self-referential link in it. I always do that, never know what might happen to an HTML file. The link, however, was to a web site long since gone, which today renders as a parked 404 page somewhere. Of course, the paper had moved somewhere else, and of course, Google knew where it was. But it was still funny to see that old link.

  • Being a fact witness doesn't pay well. I received a $55 check for my time; the deposition was an all-day event.

Tuesday, February 09, 2010

modjewel 0.2.0 - a CommonJS require() function for the browser

I decided to get off my butt and bring my "for the browser" CommonJS require() implementation at http://github.com/pmuellr/modjewel into the 20-teens.

some background

Just over a year ago the "ServerJS" group was formed based on the "What Server Side JavaScript needs" blog post from Kevin Dangoor. This effort was renamed "CommonJS" later in the year, and lives on the web here: http://commonjs.org/. Kris Kowal has a nice write up on CommonJS at Ars Technica in the article "CommonJS effort sets JavaScript on path for world domination", if you don't know anything about it. Note: I'm a sucker for software-based world domination.

One of the first items looked at by the CommonJS community was a "module" system, centered around a function named require(). More information on modules and require() is available here: http://wiki.commonjs.org/wiki/Modules/1.1. While most of the folks in the group were playing with server-side JavaScript, I was interested in the browser, so cooked up a crude version of require() in a github project just over a year ago; the crufty old hack is here.

I figured someone else would do a proper version, but haven't really come across anything besides David Flanagan's efforts, documented in his blog posts "CommonJS Modules implementation" and "A module loader with simple dependency management". I have to admit I don't watch the CommonJS stuff all that closely, I probably missed some other implementations.

David's code doesn't have any kind of a license associated with it, so I didn't really feel it was appropriate to use it for a real project. And there were some things I wanted to modify. So, at the very least David shamed me into sprucing up modjewel and I will admit to stealing bits of his design as well.

One thing I learned from David's blog post was that compliance tests exist. Loves having tests. They currently live here: http://github.com/kriskowal/commonjs. Note that David's blog post points to an older version of the tests.

Onto the goodies in the newly improved modjewel ...

nice ui for the compliance tests

As I mentioned above, there's a set of test cases for the require() function. Problem is, they're built for command-line invocation; remember, this CommonJS stuff is more server-side than client-side JavaScript. I was recently pawing through the source for the SunSpider JavaScript benchmarks, and realized I could use the same "run the tests serially in IFrames" technique here, so I lifted that design. I now have a single page that will load and run all the tests serially, and produce a green/red report card when done. It's not perfect, but it's very handy.

One weird thing to note was that I couldn't find any kind of license on the tests; I ended up importing them directly into my project, which is kind of silly. I should probably run them right out of the github repo.

support for module.setExports()

The existing facilities with modules make it easy to provide access to a number of objects within a particular module. However, for those times you only have a single object in a module, things get verbose. Here's an example: say you want to build and use a module called WebDav (so the file is named WebDav.js) which exports a single function, WebDav.

In the file WebDav.js, you would define the WebDav class as:

   exports.WebDav = function WebDav() {
       // the function is 'named' for debug purposes
   ...
   }

You can then use the WebDav class in another file as:

   var WebDav = require("WebDav").WebDav

All the RY of Java. The "WebDav" passed to require() specifies the module, and the next WebDav selects the function as a property of the exports object the module populated.

Alternatively, with the module.setExports() support you can define the class in WebDav.js as:

   module.setExports(function WebDav() {
   ...
   })

and then use it in another file as:

   var WebDav = require("WebDav")

Better! Very convenient if you have a 1-1 correspondence between classes and modules.

module.setExports() isn't official yet; see the wiki page for more information: http://wiki.commonjs.org/wiki/Modules/SetExports.

preloading modules via <script src="">

The way modjewel's version of require() works is like every other browser-based one: it use synchronous XHR. Which is ok for development-time, but completely unacceptable at deployment-time. So they say. Flanagan came up a preload story for his require2.js file, but the mechanism seemed a little crude.

Instead, I created a command-line utility which will convert a module into a file which can be embedded with <script src="">. So then the story would be that you run this utility as part of a build step, perhaps minize or whatever you kids do to your JavaScript afterwards. In the end, you have a "plain old" JavaScript file to "include". All the utility really does is wrap your code in a function. When this code is executed, it registers the module and the function, such that the first time require() is called to load the module, the function is executed and the module is loaded.

A nice aspect of this is that the order in which the <script src=""> are placed doesn't need to be in some kind of special pre-req order. Including the modules doesn't really load them, it just pre-loads them.

Anyway, it's one answer to the "you can't use synchronous XHR!" argument.

portability

I've run the tests successfully on Mac Chrome, Mac Safari, Mac Opera 10, the IPhone Simulator, Windows Chrome, and Windows FireFox 3.0. IE 7.0 was a no-go.

There is an issue with Chrome, where some tests fail when running against a file:// URL. See issue 1 for more info.

There are some jiggly issues with Windows - of course those being path separator issues (\ instead of /) and new-line issues (CR LF instead of LF). Should be easy to fix. Not sure what to do about IE 7, do I use FireBug Lite to debug that?

Monday, February 01, 2010

remote web inspector - baby steps

Last week, I did a little work getting a remote debug story between an iPhone and WebKit's Web Inspector limping. Barely limping. More like crawling. Anyhoo, the write-up is here:

http://muellerware.org/papers/remote-web-inspector-baby-steps.html

Wednesday, December 02, 2009

who called that function?

A question came up today in Web Inspector land - someone wanted to know why setTimeout() was being called so often in some web application. The suggestion was that a capability could be added to Web Inspector's timeline panel to show a function's invocations (setTimeout() in this case) as well as the stack traces for those calls, to figure out who was actually calling the function.

My immediate thought was - can't you do this with user-land code? If you can do it with plain old JavaScript, then I see less of a need to add the function to the debugger. Especially since:

  • The timeline already collects a LOT of information, keeping track of stack traces just adds more. The UI will get even busier, and Web Inspector will eat even more memory.

  • Some new gestures would be required to manage the list of functions that you wanted to see on the timeline, and whether you wanted the stack traces or not (if optional). Note that it's not possible to use the existing breakpoint gestures for this, as you can't set a breakpoint on built-in function (like setTimeout()).

I wasn't sure you could do this with user-land code, so I felt the need to figure out if you could or not. I got close, maybe close enough, and the result is up at GitHub as a gist with a wrapWithBackTrace() function. The idea is that you pass the function you want traced and back traces displayed for, into the wrapWithBackTrace() function, which returns a new function to use in place of the original. Invoking that function will dump information on the console, including a back trace of functions that called this function. I think there are enough comments in the code to figure out what's going on, but here are a few caveats:

  • The existing code makes use of Array's new methods indexOf(), map(), and forEach(). How nice to have these! If you don't have them, there's a link in the code to a site to get portable versions of them. Note that all the browsers I tested with (listed below) seemed to have them already. Wonderful!

  • The whole functionality revolves around the bizarre caller property of Function instances. Look it up in your favorite JavaScript reference. Supposedly this property will disappear in future implementations of JavaScript engines, and is already not available in some (IE, I think).

Some sample code that uses the wrapWithBackTrace() function is available in the same gist. Here's a bit of it:

function legend(message) {
    var dashes = new Array(60).join("-")
    console.log(dashes)
    console.log(message)
    console.log(dashes)
    console.log("")
}

// tests with user-land function

function factorial(n) {
    if (n <= 0) return 1
    return n * factorial(n-1)
}

function printFactorial(n) {
    console.log(n + "! == " + factorial(n))
    console.log("")
}

function a() { b() }
function b() { c() }
function c() { 
    printFactorial(0)
    printFactorial(5)
}

legend("calling factorial() before wrapping")
a()

// install the replacement function
factorial = wrapWithBackTrace(factorial)

legend("calling factorial() after wrapping")
a()

legend("calling factorial() at top level")
factorial(0)    

This code exercises some of the edge cases for the function - recursive functions, and functions run at the top level of a script (no back trace available). The output of running the code above, in the console, is:

-----------------------------------------------------------
calling factorial() before wrapping
-----------------------------------------------------------

0! == 1

5! == 120

-----------------------------------------------------------
calling factorial() after wrapping
-----------------------------------------------------------

backtrace for factorial()
   - printFactorial()
   - c()
   - b()
   - a()

0! == 1

backtrace for factorial()
   - factorial()
   - wrappedWithBackTrace()
   - ... recursion on factorial()

(... repeated invocations elided ...)

backtrace for factorial()
   - factorial()
   - wrappedWithBackTrace()
   - ... recursion on factorial()

backtrace for factorial()
   - printFactorial()
   - c()
   - b()
   - a()

5! == 120

-----------------------------------------------------------
calling factorial() at top level
-----------------------------------------------------------

backtrace for factorial()
   - {top level call}

Sample code is also provided that wraps setTimeout(), which worked the same way. Just wanted to make sure it worked with built-in functions as well as user-land functions.

The code works in the latest WebKit nightly, Safari 4.0.4, Firefox 3.5.5, Chrome 4.0.249.12, and Opera 10.10, all on Mac OS X 10.6. Note that Opera does not have console.log(), so I gave it one:

console = {log: opera.postError}

The code also works node.js. The node system also doesn't have a console.log() function, so I gave it one:

console = {log: require('sys').puts}

Actually, there's one tiny little WIBNI with node that shows up in the output - can you spot it? (you'll need to install node and try it yourself)

In the end, I kinda doubt the usefulness of this - the problem being that so much of JavaScript these days is anonymous functions. Without being able to get the "file name" and line number a function is defined on, and without a name for the function itself, having your backtrace be a list of {anonymous} is less that helpful. Seems like it would be handy to have a getStackTrace() method on Arguments to give you this information. This still wouldn't help with eval()'d code though, as it doesn't even have a "file name" associated with it.

Tuesday, November 03, 2009

evil eval

With all the nascent alt-JS things people are playing with, talked about on this InfoQ blog post, I got to thinking a while back how we could make debugging a little better for these folks in WebKit's Web Inspector.

With my familiarity of Java's innards, I happened to think about supporting a mode of using an alternative source file, and line number mappings between the original source and the generated source, to allow folks to have some level of source-level debugging in their original language. That would be for systems which actually generate JavaScript from the original language.

Java supports meta-data in it's .class files for this sort of information, and I've seen it used for things like debugging Groovy in Eclipse using the stock Java debugger. Not a perfect story, but useful.

So I opened up Bug 30933 - Web Inspector: support for debugging alt-JS files which generate JS. Feel free to drop ideas there.

Anyhoo ...

While chatting with someone who could make use of this, they noted that their biggest problem was that eval() doesn't do a very good job of providing information about syntax errors in the eval()'d code. Really? Time for some experiments.

First, why care about eval() in the first place? Since it's evil. If code is being generated from some alternate source, don't you end up with JavaScript code you can just use in the browser with a <script> element? Sure, you can do it that way. But why do an off-line compile if you could do the compile in the browser?

It's also the case that some 'traditional' JavaScript libraries like Dojo may end up (depending on your configuration), downloading your source via XHR and then running it via eval() instead of using <script> elements.

Getting good diagnostic information from eval() would be good for lots of people.

On to the experiment:

Here's the source of an HTML file to try out with different browsers. There's a link in the HTML which, when clicked, will run the clickMe() JavaScript function. That function builds a string which will be passed into eval(). I added 100 or so newline's in the source before a string that all the browsers would actually choke on (took me a minute; my first guesses at things that would cause syntax errors didn't). The 100 empty lines are to see what kind of line numbers JavaScript will tell us about, if at all; if line numbers with syntax errors are reported, we want to see a line number like 100, not 13 (the line in the source that invokes eval()).

The page is also available on my web site.

Below the source are the results you see in the browser after clicking; these are the properties of the exception object generated by eval(), one per line.

HTML source for test:

     1  <html>
     2  <head>
     3  </head>
     4  <body>
     5  <a href="javascript:clickMe();">click me!</a>
     6  <p>
     7  <div id="output" style="font-family: monospace"></div>
     8  <script>
     9  function clickMe() {
    10      var lines99 = new Array(100).join("\n");
    11      evalString = lines99 + "for ]](x does not compute)]]\n"; 
    12      try {
    13          eval(evalString); // this is line 13 of the source file
    14      }
    15      catch(e) {
    16          dumpException(e);
    17      }
    18  }
    19  
    20  function dumpException(obj) {
    21      var result = "Exception:<br>\n";
    22      for (var key in obj) {
    23          result += "   " + key + ": " + obj[key] + "<br>\n";
    24      }
    25      document.getElementById("output").innerHTML = result;
    26  }
    27  </script>
    28  </body>
    29  </html>

WebKit nightly r50423 on Mac (basically, Safari):

Exception:
   message: Parse error
   line: 100
   sourceId: 4837249224
   name: SyntaxError

Opera 10.00 Build 6652 on Mac:

Exception:
   message: Statement on line 6: Syntax error stacktrace: n/a; see opera:config#UserPrefs|Exceptions Have Stacktrace
   opera#sourceloc: 6
   stacktrace: false

Google Chrome 4.0.223.11 on Mac:

Exception:
   message: Unexpected token ]
   stack: SyntaxError: Unexpected token ]
    at clickMe (http://muellerware.org/exception-in-eval.html:12:8)
    at unknown source
   type: unexpected_token
   arguments: ]
   name: SyntaxError

Firefox 3.5.4 on Mac:

Exception:
   message: missing ( after for
   fileName: http://muellerware.org/exception-in-eval.html
   lineNumber: 112
   stack: eval("[100 \n's elided]for ]](x does not compute)]]\n")@:0 clickMe()@http://muellerware.org/exception-in-eval.html:13 @javascript:clickMe();:1
   name: SyntaxError

IE 8.0.7100.0 on Windows 7 RC:

Exception:
   name: SyntaxError
   message: Expected '('
   number: -2146827283
   description: Expected '('

Google Chrome 3.0.195.27 on Windows 7 RC:

Exception:
   message: Unexpected token ]
   stack: SyntaxError: Unexpected token ] at clickMe (http://muellerware.org/exception-in-eval.html:13:8) at unknown source
   type: unexpected_token
   arguments: ]
   name: SyntaxError    

Yikes, not a pretty picture! WebKit was the only one that got the line number I wanted. But it gave a pretty crappy "message". Nice seeing the "stack" entries for some of the implementations.

To be honest, things like the "message" - the reason why you got the syntax error - I expect to be somewhat implementation dependent. Although I expect something (looking at you, JavaScriptCore!).

There's a question of semantics, of course. The exception generated from WebKit, with the line number of the eval()'d source, would be confusing had I not caught the exception (though perhaps I'd see something different in the debugger had I not caught the exception). In reality, what you may really want to see, structurally, is something like Java's nestable InvocationTargetException, for eval(), because there are multiple levels of things going on here; for the ultimate evil, eval() may end up doing some of it's own eval()'s.

In any case, it would be nice to see some of this stuff standardized. In ECMAScript 6, I guess.

Sunday, October 04, 2009

Dropbox HTML experiments

Dropbox. Have you heard of it? If not, a colleague Jason Wagner recently wrote "Have You Heard Of Dropbox?", which is a good introduction with gushing praise. Steve O'Grady also recently wrote "There's More to Dropbox Than Piracy", which is a good read.

OK. So I installed it. On my laptop and my iPod Touch. Tried a few files. Nice.

The iPod Touch / iPhone version of the app does all it's own rendering of the shared files as near as I can tell - that is, it renders them in the Dropbox app, rather than launching another app to view/edit the shared files. So I thought - does it handle HTML files? Let's try.


experiment 1 - simple HTML page

For the first experiment, I created an HTML file with some embedded styling and JavaScript.

Here's the HTML:

<div style="font-size:300%; font-face:Optima; text-align:center;">
<p>experiment-1:

<p><a href="javascript:alert('it works')">click me</a>
</div>

Note that I created and subsequently edited this file right from the Dropbox folder on my laptop.

Trying it on my iPod - it works! Here's the screen after pressing the "click me" link, showing the alert:

Next, I dropped my network connection, and tried again.

Woops. Turns out that the Dropbox iPhone app only downloads a file for offline viewing when you mark the file as a 'Favorite'. OK, easy to fix. Connect to the network, view the page, mark it as a Favorite, disconnect, try again - it works!

BTW, what an ugly "fail whale"!


experiment 2 - external JavaScript, CSS and image

That worked out ok, so let's try something a little more challenging. Basically the same test, only move the styling to a separate CSS file, move the JavaScript to a separate file, and reference a shared image.

Here's the HTML:

<link rel="stylesheet" href="experiment-2.css">
<script src="experiment-2.js"></script>

<div>
<p>experiment-2:

<p><a href="javascript:clickHandler()">click me</a>

<p><img src="experiment-2.jpg">

<p><i>"kittens"</i> from
<a href="http://www.flickr.com/photos/mathias-erhart/2562106102/">
http://www.flickr.com/photos/mathias-erhart/2562106102/
</a>
</div>

Here's the JavaScript:

function clickHandler() {
    alert('it works');
}

Here's the CSS:

div {
    font-size:  300%;
    font-face:  Optima;
    text-align: center;
}

And here's what I get when I view the HTML file:

Woops. None of my referenced files were loaded. On a hunch, I decided to "Favorite" all the files. After doing that, it works!

You can just make out those adorable kittens behind the alert dialog. You'll have to load the page yourself to see them in all their adorable-ness.


experiment 3 - using localStorage

OK, time to get serious. Does this thing support localStorage?

Here's the HTML:

<div style="font-size:300%; font-face:Optima; text-align:center;">
<p>experiment-3:

<p>This page has been viewed by you <span id="times">???</span> times.

<div id="message" style="color:red" ></div>    

</div

<script>
(function() {

var timesSpan  = document.getElementById("times");
var messageDiv = document.getElementById("message");

function log(message) {
    messageDiv.innerHTML += message + "<br>";
}

if (!this.localStorage) {
    log("localStorage is not available!");
    return;
}

var times = localStorage.getItem("times-run");
if (!times) times = "0";
times = parseInt(times);
times = times + 1;
timesSpan.innerHTML = times;
localStorage.setItem("times-run", "" + times);

})();
</script>

And here's the page when loaded, then reloaded a few times. It works!


Just a few experiments, whetting my appetite for more. I assume it must support SQL storage as well. What about app-cache? XHR to another shared file?

Where am I going with this? Well, seems to me like if could create a little "application" as a single HTML file - embedded style, JavaScript, data: URLs for images, etc - then you have a nice little way of deploying your application. Just leave it in your Dropbox folder, edit away, test on your device, rinse and repeat. Editing such a wad would be a bit painful. You could write the application as pieces, like in experiment-2, then use a pre-processor to stitch the bits together and copy to your Dropbox (or provide a link to it).

Seems like there are many aspects of deploying a little app in this way which would be much nicer than how you have to do this using Mobile Safari. Using Dropbox would be a bit of a low-fi experience in comparison, but might be easier to write and update.

All three experiments are available in my public Dropbox folder, which you can access via your browser:

Friday, September 18, 2009

JavaScript tracing

Usually when I'm trying to diagnose a problem with some piece of code, I'll reach for a debugger. A debugger is a great tool to use if you want to watch your code as it's executing, to see where it goes, what it touches, etc. But sometimes, using a debugger is inappropriate. For example, timing problems in your code can be difficult-to-impossible to diagnose with a debugger, because the act of debugging changes the timing (the observer effect).

In these cases, I reach for a tracer. What's a tracer? A tracer is a debugger, of sorts, that shows you what functions are being executed, and produces some kind of hierarchical log of the function execution when you are "done". Here's an example of a tracer log:

ElementsPanel.reset() ➊
   ElementsPanel.rootDOMNode() ➋
      ElementsTreeOutline.rootDOMNode() ➌
   ElementsPanel.focusedDOMNode() ➍
      ElementsTreeOutline.focusedDOMNode()
         ElementsTreeOutline.revealAndSelectNode()
   ElementsPanel.searchCanceled()
Panel.searchCanceled()

In this example, the function ElementsPanel.reset() ➊ calls the function ElementsPanel.rootDOMNode() ➋ which calls ElementsTreeOutline.rootDOMNode() ➌. After the call to ElementsTreeOutline.rootDOMNode() returns, ElementsPanel.rootDOMNode() then calls ElementsPanel.focusedDOMNode() ➍. And so on.

You could instrument your code by hand, using printf() or whatever, but that's tedious. Let the computer do the work for you.

In a previous life, as a Java developer, I've written at least two Java tracers using the JavaTM Debug Interface (JDI). Once you figure out how to use JDI, it's straight-forward to write single-purpose debugging clients like a tracer.

In my current life, I'm a JavaScript developer. There is nothing like the JDI for JavaScript. Yet. Bummer. But all hope is not lost. Turns out it's fairly straight-forward to write a tracer for JavaScript "classes". Here's one: function-tracer.js. And here's how I used it in a real-life situation.

I'm currently looking at a bug which smelled to me like it had a timing issue. I knew the suspect classes, so I just traced all of them, using the following bit of code added to my HTML:

<script src="function-tracer.js"></script>

<script>
FunctionTracer.logFunction = function(message) { WebInspector.log(message); };

FunctionTracer.traceFunctionsInObject(WebInspector.View.prototype, "View.");
FunctionTracer.traceFunctionsInObject(WebInspector.Panel.prototype, "Panel.");
FunctionTracer.traceFunctionsInObject(WebInspector.ElementsTreeOutline.prototype, "ElementsTreeOutline.");
FunctionTracer.traceFunctionsInObject(WebInspector.ElementsTreeElement.prototype, "ElementsTreeElement.");
FunctionTracer.traceFunctionsInObject(WebInspector.ElementsPanel.prototype,       "ElementsPanel.");
</script>

What's going on here:

  • Add the function-tracer code via the <script src=> element.

  • Add a custom logging function which writes to a logger that my application uses (WebInspector.log)

  • Instrument the suspect classes (WebInspector.View, etc)

"Instrument"ing a class means that when an "instance" method in the class runs (a function in the class's prototype), it will get logged by my logger. The tracing output sample above was copied straight from my logger. I'll let you figure out how it actually works.

The code I posted handles new age ECMAScript 5 getter/setter accessors, which the code I'm debugging is using. I think I've provided enough checking in the code that it will work if you're running on some crusty old ECMAScript 3-ish engine.

In the end, I believe I found the issue with the bug in question, by comparing a "successful" run of the code with a "failure" run. But what's really the most fun thing of all with a tracer, is to see how bloaty your code is. In this case, I not only found a problem in the code, I could see that it was re-running some of the same code, over and over, probably for no good reason. But that's another bug.