Links

pmuellr is Patrick Mueller

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

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.

Wednesday, July 15, 2009

web beep()

I've blogged before about making use of audible feedback for debugging. One of the things I'd like to eventually get around to doing for WebKit's Web Inspector is to add first class audible breakpoints. Just like you can "click" on a line to add a breakpoint where the debugger will stop the next time that location is reached in your program, an audible breakpoint wouldn't stop, but play a sound clip instead.

There's some infrastructure that needs to be added to Web Inspector before tackling that feature, so I figured I'd go on the cheap instead. Using HTML5's new audio element, I created a beep() function which just plays a sound clip when invoked. Wherever you might put a console.log() invocation, you can put a beep() invocation instead. Actually, I created a boop() function also; a single sound is rather boring.

Of course, having to modify your source for debugging is so 1980's. So my beep() functions can also act as wrappers. Pass them a function, and they'll return a new function you can use as a replacement of the original function. When that new function is invoked, it beeps (or boops, or whatever), then invokes the original function. The new function also contains a reference to the old function, so you can unwrap as needed. Using this, you can augment addressable functions from within your REPL-y JavaScript debugger console, without having to muck with your source.

The sample code is up here: web-beep. Seems to run fine in Safari and FireFox 3.5.

Wednesday, July 08, 2009

A Survey of Data Display in JavaScript Debuggers

My mom has been asking me about the differences in the way data is displayed in some JavaScript debuggers, so I wrote something up for her.

Sunday, June 14, 2009

offline web application cache abuse

I've been a user of hand-held computers since 1997, when IBM bought me a Palm Pilot to use in a customer demo I was putting together. Since then, I've been through a number of Palm devices, and up till last week had been using a Nokia N800 device. This week, I caved, and bought myself an iPod Touch.

One of the main uses I've had for these devices is reading. On the Palm, I used the wonderful iSilo program to convert HTML to some binary format that the Palm reader rendered as good as it could - good enough - even on a 160x160 pixel display. On the N800, HTML content could just be copied to an SD disk, and then you could view it via the built-in web browser using file: URLs.

It turns out that I've been able to find plenty of material over the years, in HTML, that renders well enough on my hand-held devices. Either ready made, or built via scraping, or whatever.

what to use on an iPhone or iPod Touch

So, what are the options for reading on the iPhone/iPod Touch? Stanza is the only generic reader I've looked at so far, and it's not bad. It's particularly nice to have access to FeedBooks and Project Gutenberg books, which can be downloaded and then read later if you aren't online. Compared to Google Books which appears to only support reading while online. There's also Kindle, but I don't have any need to buy books, there's plenty of free content to be had. More importantly with Kindle is the 1995 level of HTML support, and that's a big problem for me; I like to read technical content with code samples, etc.

Beyond generic readers, there are also content-specific readers from content producers like the New York Times, the BBC, and AP News. The BBC one is especially strange. When displaying a full article, they seem to be displaying the same content that's on their web page. Various navigation and other links eating up space in the left and right side of the page. You can double-tap the middle content to get that to zoom, but why wouldn't they do that in the first place? If you actually happen to click a link on the page, even to another story on the BBC, it exits their app, and launches a browser on that page. Amazing, and not in a good sense.

Stanza seems like the most useful reader at present, at least for literature. I'm not really happy about the page-turning metaphor; I prefer to scroll pages vertically. I can probably learn to live with it.

But how can I get my own content on there, or more generally, rando HTML content on there?

EPUB

If you look at the formats of "documents" that Stanza supports, one particular format is EPUB. I've not been able to make my way through the morass that is the specifications surrounding this format, but if you're interested, here's how to get started: an EPUB file is a .zip file, expand with your favorite zip utility; you'll find a number of XML files inside, you can kinda get a clue for how everything fits together by browsing those files.

The "meat" of an EPUB document consists of XHTML, CSS, and image files. Hmmm. Sounds like the web. Could you take some existing web site and easily "EPUB" it?

HTML5 Offline Application Cache

Turns out, there's something in HTML5, and more importantly, available on iPhone and iPod Touch devices, that can take an existing web site and make it available even if you aren't connected to a network. There is a W3C Working Group Note for this available as"Offline Web Applications". Apple has documentation on their support of this in the document "HTML 5 Offline Application Cache". See also the WHAT-WG version under development.

The basic idea is to do the least possible work. You need to create a new "manifest" file which lists all the files which should be cached for offline usage. And you need to identify that file in the <html> element of your document. That's all! The browser will arrange to cache all the content listed in the manifest.

I decided to try out how well this works with Mark Pilgrim's "Dive Into Python" book. I downloaded a copy of the book as separate HTML files, ran find on the result to create the manifest, whacked the <html> elements with a multi-file search-and-replace in my text editor, set up the files on my machine's local server, browsed to it from my iPod Touch, worked like a champ.

It seems to take 20-30 seconds to get all the files downloaded, and during that time there is no indication of what's going on in the browser. I was tail'ing my server's access log to tell when I was done. Presumably some of those events specified in the WHAT-WG version of the spec will help out with issues like that. After the files got downloaded, I could turn the wifi off on my device, and continue to browse the content.

"Dive Into Python" seemed like a good test case; non-trivial markup, especially code snippets; a significant amount of content (3MBs fully expanded), and presumably not really designed to be read on a hand-held device. I'm satisfied with the results, though I think most people will find the font sizes too small. The font sizes are too small for me when displaying in portrait mode, so make sure you try landscape as well. Tough decision point there, make the fonts bigger and then you'll either get text wrapping or have to do side-to-side scrolling.

I've got the files available on one of my servers, http://diveintopython-cached.muellerware.org/, which I'll leave up until my hosting provider kills me - each access to the page from an enabled browser will download the whole book (if it's not already been downloaded). Presumably this might work on other WebKit-based browsers, like the one on an Android device, or the Palm Pre.

On the iPhone or iPod Touch, after displaying the initial page, add a bookmark by clicking the + button, and then the "Add to Home Screen" button. Remember to wait 30 seconds or so after the initial page load before doing this, to make sure all the content gets downloaded. This will add the book as a new "app" on the main application screen. I've also done the appropriate horkiness to get the cover of Mark's book to show up as the application icon.

questions

This experience raises a number of questions regarding the HTML5 Offline Application Cache support:

  • How much space does the browser set aside for this cache? I assume there's a fixed upper limit for the cache, but does it compete with all other browser caching? Or is there a per-site or per-url cache limit?

  • How can I be informed when my cache content is thrown out of the cache? Is the entire set of files listed in the manifest thrown out atomically?

  • Is there enough functionality in the existing and proposed APIs to make reliable use of this capability?

I'm left wondering if it wouldn't just be better off to provide a full-function JavaScript API to the cache in general, although clearly this could be done as well as this declarative approach. My fear is the declarative approach might take you 80% of the way there, but leave out critical capability in that missing 20%.

More experimentation required. Have at it.

Or maybe this is just simple abuse of the capability. Instead of caching web sites, you could certainly get more control by having all the book content stored in a client-side SQL database, and then build an HTML application to navigate through it. It's just a lot more work.

Thursday, June 04, 2009

debugger friendly

(this is a bit of a followup to my previous blog post - "debugging")

Hit a milestone yesterday, submitting my first patch to WebKit, in support of "Bug 25474: Show the filename and first line for '(program)' in the Profiler/Debugger". The patch provides the same support that Firefox provides to allow JavaScript eval() invocations and Function() constructor invocations to name the code they are injecting into the environment, by adding a comment to the source interpreted:

//@ sourceURL=some-url-goes-here

Presumably some-url-goes-here is replaced in your source by some kind of moniker that will be useful to you.

Why is this useful? Here's what your "list of scripts" looks like when developing a Dojo app, without this support: [link]; here's what it looks like with this support: [link]. A little more useful.

Last month, another interesting feature for debugging was added to WebKit - the ability to 'name' anonymous functions, after they've been created. A full description of this, and the value it provides, was written up a blog entry "Building a Better JavaScript Profiler with WebKit". In a nutshell, you can add a property displayName to a function which will be used as it's name in the debugger and profiler if the function doesn't otherwise have a name.

Quick notes:

  • the sourceURL feature is not yet committed to WebKit, and even if/when it is, would only be available in WebKit nightlies.
  • the displayName feature is only available in WebKit nightlies right now.
  • the sourceURL feature does not currently work with the WebKit profiler, just the debugger.
  • the displayName feature works in the WebKit profiler and debugger.
  • Firefox supports (and invented!) the sourceURL feature, but AFAIK does not support the displayName feature.
  • Dojo currently has support for sourceURL feature, but only if you're running in a Mozilla-based browser - chime in here for WebKit support.
  • Dojo has no support for the displayName feature - chime in here on that issue.

enhanced object display

Here's what I'm going to look into next:

In the WebKit bug, "Bug 25724: Incorrect collection output for Javascript console evaluation", the reporter claims that the output when displaying the result of a jQuery function invocation is incorrect. Incorrect isn't fair; it's better to say that it's not what the user expected, but then, as jjb mentions in comment #6, "we can't read [the user's] mind".

It's certainly true that the current output isn't useful, and it's certainly the case that the developer of a debugger can't read a user's mind, so the obvious answer is to let the user decide how to see their output. In Smalltalk, the debugger would call debugPrintOn: instead of the more typical printOn: method (printOn: being analogous to toString in Java or JavaScript). This allowed developers to provide a debug-friendly version of the human-readable representation of an object, without impacting runtime code. JavaScript debuggers should do the same thing.

The other bit that needs to be done is to somehow augment the "properties" that you see associated with an object in the debugger. Literally what you see in the debugger is the implementation goop of an object. Not terribly useful, especially if values associated with objects are computed lazily, via a function invocation. Seeing a function instead of perhaps the lazily computed value provides little value (though it is obviously much safer!).

What I'd like to do is provide an optional method on a function which can be called to obtain "debug properties". For instance, the debug properties for a jQuery object might return the array of matching DOM elements. DOM elements themselves might return, instead of the goop currently returned, their parent DOM object, their children, and their next/previous siblings. These DOM element's might have a "debug toString" that actually showed the HTML representation of the node (which the debuggers already support in some cases).

Of course feel free to post additional ideas on this to the existing but report.

soundpoints

Here's something I did back in the Smalltalk days; would be fun to do this for JavaScript as well:

We all know what breakpoints are, but I extended the concept in Smalltalk with soundpoints (stole the idea from somewhere). Instead of having the debugger pause the execution of the program when a breakpoint is reached, the debugger would play a sound when a soundpoint is reached. The nice thing about this capability is that you get some feedback without having to stop your program. You can set soundpoints to let you know when you venture into code you're not expecting to venture into (some kind of beep/boop), or set a very short blip sound for a soundpoint you expect to hit a lot - giving you a kind of geiger counter. Since WebKit already has programmatic support for playing audio, the "hard" part of this is already done. What's needed is to integrate into the debugger framework. OK, that is the hard part.

As sort of a side issue, when I did this in Smalltalk, I was able to do this with user-land code; I didn't need to be some kind of core developer to get the function added. That's not true today in WebKit. You need to modify existing WebKit code to add functionality like this; there's no simple way of me just adding it to my environment. That would be a nice thing then also, having a way of extending the debugging environment through plugins or equivalent. See "Bug 24273: Inspector Plugins or Extend Capability". Also note that I believe FireFox has or will have the ability to extend FireBug via plugins. I think it's too much to ask to have portable plugins, at this point, but something to think about.

what else

What else can we do beyond all that? What are your frustrations with JavaScript debugging?

Tuesday, April 28, 2009

debugging

Apparently, I tend to write a log about debugging. To me, it's the other "fun" part of programming, the first fun bit being writing code to begin with. Debugging comes into play, in that oh so rare case, where the code doesn't do what you meant it to do. Like writing code, I tend to view debugging as an art, more than a science. Especially after seeing how some people debug code - I've got some scary stories I can share over a beer.

And so it was interesting to see the subject of debugging come up in relation to the issue of "tail calls" in Python, in Guido's post "Final Word on Tail Calls". Specifically, Guido claims that usage of tail calls "does make debugging harder". No doubt, assuming your debugger doesn't realize what's going on.

Guido also gives an example of why I like debuggers:

The elimination of stack traces for some calls but not others would certainly confuse many users, who have not been raised with tail call religion but might have learned about call semantics by tracing through a few calls in a debugger.

Learning is the key. There is really no better way to visualize how your program operates than to watch it running, line by line. Typically, if you have everything set up right, you can also learn how those mysterious frameworks and library functions you're using work as well, by tracing into them. This is especially important if the frameworks and functions you're using are underdocumented; which they probably are.

Debugging also came up in reference to new WebKit goodies, as mentioned in "WebKit's week - #8", and more specifically, "Bug 25171: It should be possible to manually set the name of an anonymous function". I tried a little test, but noticed the debugger itself doesn't really have a place to make use of this information; but the profiler does. My little test made use of a decorator, like so:

function function_named(name, func) {
    func.displayName = name
    return func
}

You could use a function like that to wrap anonymous function usage if you don't otherwise hold onto the function in a variable or property.

I LOVE to see changes like this, made by the implementations. Eventually something like this might make it into a standard, but we need implementations to figure out the best way to do it before we standardize on it.

Other candidates to aid in debugging:

  • toStringDebug() - this method would get called, if available, from a debugger when it's showing you a value. Stolen from Smalltalk's #debugPrintOn: message that did the same thing. As in Smalltalk, you'd add this method to Object, with the default implementation returning the result of this.toString()

  • Ability to "name" eval()'d code. In WebKit's Web Inspector, these show up as "(program)" in the scripts dropdown. FireBug is a little better, it shows you the first bit of the eval()'d code. While playing with a framework that used eval() all over the place, I ended up using a convention of making the first line in the script that was eval()'d a comment with a 'file name' at the beginning. Very useful for FireBug. But I think we can do better.

    Why not recognize a keyword in a comment instead?

    // @eval-name "foo-blatz"
    ...
    

    That would end up showing the name foo-blatz along size other script names wherever such script names get displayed.

  • Class hierarchy browsing; many of the JavaScript frameworks have a notion of building "classical" class hierarchies in JavaScript. Whether that's good or bad, dunno, but it's the way it is. It would be nice to be able to visualize these. Since each of the frameworks does things differently, there is no single way to handle this. Some options would be to invent some new meta-variables like the examples above that you could augment your "classes" and maybe even "values" with - __class__ property on an object points to it's "class", __superclass__ on a a "class" points to it's superclass, etc. Perhaps better would be a way to allow each of the frameworks to define it's own viewers by allowing the debuggers to be extended with additional code wholesale. FireBug already does this, and you could presumably hack WebKit's Web Inspector yourself to do this.

  • What else?

Wednesday, April 22, 2009

nitro_pie

One of the things I wanted to start doing in the new job was to play with the JavaScriptCore framework. But it's a C language framework, and I don't typically associate C with "play". Good news is, if there's a C API, then there's a good chance you can talk to it from Python via the ctypes module.

I started down that route a little over a month ago, and it's gotten to the point where you can do some non-trivial things with it, so I got it all cleaned up, tagged, etc. It's called nitro_pie (Nitro is the new sexy name of JavaScriptCore), is available up at GitHub, with a downloadable .tar.gz available on the downloads page.

Here's an example of what you can do with it: nps.py; nps standing for nitro_pie shell. This Python program takes a JavaScript file name as a command-line argument, and runs it. It injects a new function, print(), into the JavaScript global environment, so you can actually see it do something. And of course that function is implemented in plain old Python. Here's "Hello World", which does what you would expect under nps.py:

print("Hello", " ", "World", "!")

Couldn't resist writing a simple benchmark to sum numbers from 1 to ?, and comparing Python and JavaScript:

---------------------------------------------------
Python
---------------------------------------------------
sum(100): 5050
...
sum(100000000): 5000000050000000

real    0m30.397s
user    0m29.799s
sys     0m0.126s

---------------------------------------------------
JavaScriptCore
---------------------------------------------------
sum(100.0): 5050.0
...
sum(100000000.0): 5.00000005e+15

real    0m7.685s
user    0m7.468s
sys     0m0.081s

The floats you see in the JavaScript version are because JavaScript only has floats. Well, kind of. One of the things I learned was that JavaScriptCore actually internally supports 31-bit (or so) integers as well (as tagged pointers). I suspect the conversion to float in this case happened when I converted to a string at the end.

Another interesting factoid I discovered, not having used eval() much in JavaScript:

eval("{a:1; b:2}") == 2

Can you see why? Here's a hint. The correct syntax for what I had intended was:

eval("({a:1, b:2})")

Usual caveats apply; early code, it uses ctypes so will occaisonally go all Bus error-y on ya if you're sloppy, not many diagnostics, etc. Only runs on Mac OS X, as that's the only place I know of that has a separate JavaScriptCore library. But good news is, you just need the nitro_pie.py file, because you already have JavaScriptCore and Python installed.

I also belatedly discovered that Apple already ships JavaScriptCore bindings for Python, which you can get by doing an import JavaScriptCore But that's only the raw bindings, which doesn't take too long to implement - making them usable is the real work here.

So now that I've got this beast, it's probably time to start playing with the ServerJS stuff again, especially since no one else was looking at JavaScriptCore.

Wednesday, April 15, 2009

shiny simian scripts

We know them as Greasemonkey Scripts, but in Aaron Boodman's recent blog post, he says the new name may be "Content Scripts". Kinda boring. How about Slick Gorilla Scripts? Shiny Simian Scripts? Ach, "Content Scripts" sound pretty good I guess.

Aaron's post is chock full of meaty information; looks really good from 50K feet. Nice to see multiple files will be supported, including .css and resources like images.

I still use Greasemonkey for a few scripts I've written, but found the environment a bit limiting to get into it too deeply. Debugging was difficult, installation/deployment a bit unwieldy. And of course the endless issues having to do with writing screen scrapers, reacting to changes in your target's pages. I tried writing a few scripts for the Moz-based browser on my N800, which supports a flavor of GreaseMonkey via an system-installable update, but that was really painful. I hope we see a simpler workflow for developing these extensions, eventually.

The reason I'm so interested in this stuff is that I think user-scriptability provides a big paradigm shift. Much like writing editor macros for emacs or XEDIT or similar, it transforms an already powerful and useful tool into an extendable one. If you combine this sort of extensibility with the ability to publish/deploy and install these extensions easily, you have an actual platform on your hands.

It's easy to imagine, for instance, the sort of "content scripting" you could do, might subsume the whole "mashup" scenario; kind of flip it on it's head. Instead of having to host an environment to pull and push content from other environments, you can do it right from the primary content site.

Eventually this drives us to the point where the web applications we use are actually installed (ok, cached) on our machines, as extensions, and just push and pull data via HTTP/XMPP/whatever to data/service servers as needed. Of course, we've had a good 15 years of being able to do this, in theory, and are still stuck with icky, unusuable web sites that trap our data behind inscrutable wads of HTML. But things seem to be changing for the better here, over time; REST and all.

Something like Giles Bowkett's Hacker Newspaper could just be an extension, instead of having to be a 'site' with a 'server'. Who wants to deal with that junk?

Beyond all the content scripting stuff, Aaron also mentioned: "There is a team in Chromium working on an out-of-process version of the web inspector." This is great news. WebKit's Web Inspector is quite useful, but as an "in browser" debugger, it is also clearly totally unsuited for debugging on small devices. You know, like all those devices that are using WebKit? An external debugger is required for such situations. Something like Drosera. Both modes of debugging are nice to have - in browser and external; I want them both.

All GREAT stuff. Of course all of this is for Chrome, and not for WebKit. Which makes you start to wonder how we can get all this stuff into WebKit so all the WebKit consumers will have access. Either that, or start considering Chromium to be a platform to consume instead of WebKit. If these Chrome extensions heavily depend on things not already in WebKit, like V8, the multi-process model, etc, there would be even more reason to consider Chromium as the lower level platform rather than just WebKit.

Tuesday, April 14, 2009

ordered javascript properties

So all this time, I believed what I had read regarding the ordering of properties when enumerating JavaScript objects with the for...in statement, with this being a typical description:

A for...in loop iterates over the properties of an object in an arbitrary order.

That's from the Mozilla Developer Center JavaScript reference.

Turns out that's not quite true. See John Resig's blog post "JavaScript in Chrome", and scroll down to the section "for loop order". In particular, John writes: "All modern implementations of ECMAScript iterate through object properties in the order in which they were defined. Because of this the Chrome team has deemed this to be a bug and will be fixing it." Follow the links to the bugs John points out in his blog post for more discussion.

I couldn't resist trying this out myself, so created a little test case, here. WebKit Nightlies and FireFox 3.0 behave the same for all the cases, but Opera 9.23 acts a little differently in one of the cases.

John makes a couple of points I can't agree with, in his comments in the Chromium bug that he referenced.

  • The specification does not define how an implementation should handle it - therefore an implementation should conform with other implementations.

    What? How does not having an explicit definition in the spec imply that all implementations should conform with each other?

  • The argument that they're doing it to "keep the engine fast and lean" is bogus. There are two other, faster, ECMAScript engines out there: SquirrelFish and TraceMonkey and they both implement the looping indentically.

    John is referencing a previous comment which claimed that V8 did not respect the 'definition order' because doing so could impact memory usage and/or peformance. But the rationale John makes is that since there are two faster engines which DO implement the 'definition order', the memory/performance rationale is bogus. Which makes no sense of course. I would imagine the typical implementation of 'definition order' would trade space over speed, so you could make the property access "fast enough" by spending some additional space to maintain the order as well as the presumably speedy hash access. Complete guess on my part. But to me, it's hard to imagine that implementing 'definition order' isn't going to cost you SOMEWHERE, over not implementing it. I'm cheap, I don't like to pay for things I don't need.

So not only was I surprised by the property ordering, I'm kind of sad because it means JavaScript has some built-in waste for a feature few objects will probably ever need. Kind of like Java's monitors in every object. Ah well.

A few folks pointed out to me that upcoming EcmaScript standards would likely standardize on this ordering behavior. While it's difficult (for me anyway) to track down EcmaScript standard stuff, I did find this set of drafts which seemed ... recent ... and looking at the "23 Feb 2009" version, you can see phrases like "An ECMAScript object is an unordered collection of properties" (page 14) have been changed to "An ECMAScript object is a collection of properties". Hardly a ringing endorsement of defined property ordering, but a step in that direction, no doubt.

So, I guess change is coming. Next question, how can we make use of this?

One thought is with JSON. Typically with JSON you think of object definitions with key/value properties, all defined "at once". Meaning things like "streaming" JSON objects don't really make sense. Only now, maybe it kinda does. Or you could do something like add a property to an object first, called something like "__type__" which would provide some shape information, which a serializer could make use of, because it would see that before other properties.

That's nice. And is fairly logical. But the memory/performance impact will probably always bug me.

Wednesday, March 18, 2009

new job: WebKit

Well, it's nearly Spring, which means, for me, a new job. Right? Seems that way, looking back over the last few years. I've had some difficulty carving out some space for myself in the groups I worked with, what with the great talent already working there. Many chefs in the kitchen and all that.

For this new job, I'm on my own, at least within IBM. I'm now IBM's "WebKit Center of Competency" person. Officially, what I put on my Moo cards and in the corporate directory was "WebKit Stuff".

What does that mean? Not sure yet. But apparently WebKit is interesting enough now, that my bosses thought someone should focus on it full time. I guess I kinda helped out there, having sent various notes over the years, to my new bosses, that said things like "Why aren't we doing anything with WebKit?!?" My new bosses are Dave Boloker, and his boss is Rod Smith. I've moved over from Rational to the Emerging Technologies group.

There is some ongoing work in Emerging Technologies that is very much browser-centric, and so I'm going to be hearing this week what they're up to, and to see what I can do to help. Beyond that, my assumption is that in the future we'll have more folks needing to integrate with WebKit in various ways, so I'll start laying some groundwork for IBMers to help make that a little easier.

Beyond that, I'm particularly interested in focusing on JavaScript, and making JavaScript developers' lives better. My secret dream is to make the browser a better Smalltalk than Smalltalk. You gotta have a dream.

I was ecstatic to hear that my management expects me to be contributing code back to WebKit. One thing I haven't done in IBM is any open source contribution. There's clearly no better way to learn a code base than to have to tinker with it, and then submit that to public shaming.

So all in all, this is really shaping up to be a great job for me; subject matter that deeply interests me, the open source angle, and of course the fact that it's a scary huge mound of mainly C++ code that I'm completely unfamiliar with. What more could you ask for?

I'm definitely a little bummed that I won't have a chance to work with Dave Johnson, who will be starting a job with Rational at IBM at the end of the month. It's a small world though - I think Dave will be sitting next to me, and will be taking a spot on the Rational CTO team, which is the team I just left.

I'll also be a little bummed if something were to happen to one of my new Emerging Technology teammates, Sam Ruby.

Thursday, February 26, 2009

size matters

Fellow IBMer Bob Blainey recently posted a blog entry "Are you a Pakled?" which touched on software bloat. This is a subject near and dear to my heart, burned into me after spending a good five years or so in the embedded software space.

At the time, the devices that we were targeting would typically have something like 2MB ROM and 2MB RAM available. Think of the ROM like hard drive space, and the RAM is the memory in a computer today. We worked with devices that had less memory, sometimes, and near the end of my run, worked with devices that had much more memory available. But the 2/2 story was sort of the rule of thumb.

That's not a lot of space, so you worry about every byte. You worry about every new library that you need to add to your runtime. Not just in terms of it's disk footprint, which ate ROM, but the runtime footprint, which ate RAM. There were always hilarious situations where we'd be talking to product groups with no embedded experience, where they'd die laughing when we told them their library had to fit in 10K bytes; or it would be us laughing when they told us their runtime only required 1MB of RAM.

If you enjoy the "solving puzzles" aspect of software development, this is just an additional challenge on top of all the other challenges, which made it, for me, a terribly fun working experience.

You can imagine the "sticker shock" that hit me, when my first position after leaving the embedded world, involved J2EE. Whoa. And the sticker shock continues to this day, in every new piece of work I'm involved in. But it's not just me; most of my colleagues back in the embedded days seem to experience this sticker shock in their day-to-day jobs, which is something that I think most developers just don't think very much about. Like the kid in "The Sixth Sense", we see bloat - everywhere.

It's heartening to see the resurgence in interest in embedded development, in places like the iPhone and Android, since I hope this means we'll be seeing a lot more folks taking an interest in software bloat.

Aside: the Pakleds, referenced in Bob's post, were one of the most memorable races for me in the Star Trek series, but I wasn't able to find any video clips from the show. I did find a little audio clip though; this is Riker asking a Pakled what they need.

Wednesday, February 25, 2009

web ui progress

The hotness of the day (well, yesterday) appears to be video at 280atlas.com (permalink at Viddler here). It's technically very impressive. But I'm also a bit saddened. I worked on similar technology 15 years ago, for desktop applications. You know what I'm talkin' 'bout. I won't say the word.

Sad because, even 15 years ago, web browsers were around, in some state. Why has it taken this long for things to progress this far? It's nuts. Seems like a lost decade, to me. Of course, it's not like we were even really innovating in this space, back then, either. Interface Builder goes back even further.

Feh, always with the rock throwing. So let me shine a little brightness, as well.

First and foremost, hats off to the folks at 280north.com. Getting all this to work on the clumsy web browsers we use today is surely no small feat. It looks gorgeous. The layering is interesting, built on Cappuccino and Objective-J. Stuff to look into.

Two things I saw in the demo that I liked, in terms of UI constructs: "Layout Support" and "Split Views".

The "Layout Support" appears to be similar to what we used to call "Form attachments", back in the day, that allowed you to specify how widgets were attached to things - other widgets, parents, etc - and how they would resize. Ours were based on Motif's XmForm constraint resources. (Aside: isn't it hilarious that the first reference I found to XmForm was in the lesstif online doc?) Although kind of crazy complicated, and really easy to screw up when you were coding by hand, and requiring you implement a constraint solver IIRC, you could do some incredible stuff in terms of making windows full of widgets which responded in a nice way to resize requests. And even just doing (non-absolute) layout by specifying which things were next to other things, and letting the system figure everything out based on default sizes.

"Split Views" are similar to what we called "Split Panes" (think that's what we called them), that were a late edition to our product, but to me added the last little bit of UI goodness for users. Instead of providing either fixed width, or perhaps proportional width child windows inside a parent, Split Panes gave users the ability to resize those child windows as they saw fit. After they got added to the product, there was really not much else that I found I needed, to be able to whip up a nice little UI with intelligently resizable innards. Separate the top level bits of a window into Split Panes, and then layout those children using Form attachments, or perhaps recurse with another Split Pane.

Sure would be nice if these were native capabilities provided by our browsers. CSS (and Tables!!) and Framesets just don't cut the mustard. Ah, not to worry, we'll have a <video> element we can use in our applications soon, and that will more than make up for the lack of these other, much less interesting capabilities.

Thursday, February 19, 2009

on bespin

Could you have possibly missed the news on Bespin, a project from Mozilla Labs? Don't see how. But read up if you haven't.

Bunch of rambling thoughts here:

  • Also check out Sun's Lively Kernel. In particular, make sure you click on the "Development Tools" link (blue globe/basketball thing) after you "Enter the Lively Kernel" to see their take on Smalltalk-like browsers for JS.

  • Bespin works it's magic with HTML Canvas, Lively Kernel uses SVG. Interesting, because these are two completely different low-level programming models, though perhaps we'll end up seeing widget toolkits built with both.

  • Implementing text editors with HTML Canvas and SVG leads you into the Uncanny Valley, as such low-level things as clipboard operations, cursor movement, etc all have to be implemented with user-land code. Doesn't sound like a great proposition, it's at least a crap-load of work to get all the expected text editing behaviors implemented.

  • But you know what, I think Uncanny Valley may not really apply to text editors. I use a large number of text editors in my day-to-day computer usage, and almost none of them are using any kind of a common "text editor component". Eclipse, TextMate, Notes, vi, etc. You just have to get used to them. There are certainly programs I use that DO use a "system-level" common text editor - in my case, the one Mac OS X provides. Forms within Safari, Twitterific, other apps presenting text areas, etc, all use the baked in text editor control, which is nice, because I get the same kind of spell checking, cursor movement, etc. I think extending the usage out to "code" editing (eg, syntax highlighting) just isn't going to work though. Or let's say it hasn't happened yet.

  • Although most of the focus on Bespin is on the browser bits, the impacts/opportunities for server bits isn't lost on some people. In fact, this may well be a more interesting piece; if you imagine an interesting set of back-end "IDE services" being defined for Bespin, why couldn't desktop editors also take advantage of them?

May you live in exciting times.

Thursday, February 05, 2009

it's alive! JavaScript modules

Less than a month ago, I posted one of my long-winded blog entries, the subject being "JavaScript Modules". A week ago, Kevin Dangoor posted a blog entry titled "What Server Side JavaScript needs". One of the issues Kevin raised was:

JavaScript needs a standard way to include other modules and for those modules to live in discreet namespaces. There are easy ways to do namespaces, but there's no standard programmatic way to load a module (once!). This is really important, because server side apps can include a lot of code and will likely mix and match parts that meet those standard interfaces.

Kevin's post led to the creation of the serverjs Google group then a wiki and an IRC channel on freenode - #serverjs.

Well, yeeee haw!

While Kevin's post is really aimed at server-side JavaScript, a lot of the concepts he talks about are actually interesting to think about in the context of web browsers as well, as far as I'm concerned. And I'm not alone. There are also folks who don't give a flip about the browser and that's fine too.

With regards to "modules", there is a proposal for something that's pretty similar to what I blogged about, which I'm obvious partial to. And there are other alternatives. And spirited debate. Come join the fun!

Now there's talk of test cases. Good lord! In a week!

Saturday, January 10, 2009

spring break in canada?

My wife has this bizarre problem. She likes snow. Apparently, it's genetic, as my children suffer from this as well. Despite the fact that we sometimes get deluged with snow (per the included image), most of the time, we get nothing, or not enough to count.

I've suggested to my wife several times that we should take a vacation in Canada some time; I hear they got lots of the white stuff up there. She's taking me seriously now; desperation is setting in, it appears.

The current thought on time frame is spring break, which for us falls at the beginning of April. Seems pretty dicey to me as to whether they'll still be any snow around by then.

I've been to Mont Tremblant and the big log cabin at Montebello, for "work related meetings", both of which are the kind of thing I'm thinking about, though I'm guessing more expensive than I'd like.

Any suggestions from the lazy web?

Friday, January 09, 2009

JavaScript modules

When I see code like this, an example I pulled from YUI, I simply want to cry. Things need not be so ugly. I don't mean to callout Yahoo here, it's just an example I found. Most of the non-trivial 'package-ized' JavaScript I see is like this.

Issues:

  • Everyone of these files is a single anonymous top-level function that is invoked to execute it at the bottom of the file. Icky. Why do we need this?

    This is done because the method of compositing function into your application is done by including the source of that function into the big, single namespace known as your JavaScript environment. To keep from having source you are compositing into your app not "infect" it with additional global variables, you use the trick of putting all your code in a function body and executing it. The function can be (and should be) anonymous. No infection, or controlled infection, as long as you use var on all your variables (argh), as the variables at the top level of the function are local to the function, and not the "environment" (basically, globals).

  • We have functions defined in functions defined in functions here, two levels of which are asynchronous callbacks.

    I don't have a big beef with nested functions, except when it gets silly. Like, in this case. One of the big offenders is definition of the loader function, whose purpose is to load the code, pre-reqs, etc, defined as callbacks presumably because the loading of such files isn't necessarily provided as a synchronous capability from the browser.

  • I bet the folks that wrote this had editor macros set for the text YAHOO.example.app

    Frankly, there's no defense for this; the code should probably be using "shortcut" variables for the "package names", and even just some of the functions, like YAHOO.log.

    I assume there is some kind of taboo over using shortcut variables here; or are people depending on fully qualified function names for code completion or other source analysis tools? Yikes.

How can we make this nicer looking?

I think we need a better way of packaging up the pieces of our composite applications.

Packages. Modules. Bundles. Whatever.

Google Gears WorkerPools, again

I've previously blogged about using Google Gears Worker Pools to build service frameworks. For certain types of functionality, this makes a lot of sense. It certainly has the characteristic of compositing function into your application in a clean, infection-free manner. But it also has the following characteristics:

  • All message sense are asynchronous. While this sort of programming style might be comfortable to some people, and in fact might be the best way to program in the end, it's not terribly friendly for most programmers who have been using mostly/only synchronous function calls their entire programming life.

  • Out of the box, you're always going to be 'sending messages' instead of 'calling functions'.There's technically not much of a distinction between sending a message and a function invocation, you might say, besides the invocation style of the two. But again, for most programmers, function invocation is the norm. And probably requires less syntax per invocation. Shorter programs == good.

So while I don't have any problem with WorkerPools per se, and in fact, I think they are a great pattern for handling asynchronous, parallel work, they also aren't really going to provide the best pattern for modularity.

But I really love the cleanliness aspect.

So here is what I want

Python modules.

And here's the thing. I think we can add support for this fairly unobtrusively.

The basic idea is to define a new function, say loadModule() which is used to 'reference' another module by passing it's name as a parameter (URI to the name, prolly). A module is just a JavaScript file. Only instead of working the way <script src=""> does, it actually defines a new separate, empty namespace and loads the JavaScript into that namespace (just like the way Google Gears WorkPools does). The process of running loadModule() on a module the first time is that the JavaScript source is executed. The object returned is a 'module' object, whose propertes include all the global variables in the module's private namespace. For loadModule() calls with the same module beyond the first, the code is not executed again, but the same 'module' object is returned.

Or other varieties thereof. It's fairly simple to play with this kind of stuff from with Rhino, though your brain will be hurting after getting all the prototype and context linkages set up right. I assume you can do this sort of multi-environment stuff in other JavaScript implementations.

I want it in the browser.

This isn't the kind of code you can write in userland JavaScript, because JavaScript doesn't give you low-level access to it's innards. Needs to be yet another function the browser injects into the JavaScript environment, probably not even implemented in JavaScript, but in C, C++, Java, etc.

What changes

This makes individual JavaScript files a little cleaner by:

  • Not requiring the anonymous function wrapper.
  • Letting you get away with making your namespace a mess without worrying about infecting someone else's namespace.
  • Letting you use shorter names, because imports beyond the first are crazy cheap, so every module would probably just import everything they needed as one-level modules.

Sure would be nice if we could make that loadModule() function synchronous, otherwise loadModule() would really have to be a function which took the URI to the module and a callback, and invoked the callback after the module load. Back into some ickys-ville. Is <script src=""> synchronous?

It's not a lot. But it's a start.

Additional advantages

  • It's easy to imagine that the process of reloading a module which has changed (you just edited it while you were debugging) would be a little more straight-forward; largely only the module itself is affected, though presumably there are some imported object references that would also need to be fixed up (using short-cut variables causes issues here - is that one of the reason the Yahoo example used fully-qualified names?). Some lower-level VM help could get even those references fixed up, I'm thinking.

  • Better than eval(). Yeah, you could code something up to do this using eval(), I suppose. Or get close. The problem with eval() is the code becomes disassociated from it's source location. This makes it difficult/impossible to debug. Or save, after I make my changes in the debugger (some day). With an import story, the original location of the source can be associated with the code, just like all the files <script src="">'d into your page get associated with their source location.

  • You could imagine the keeping byte- or machine-code versions of those modules, in their pre-executed state, cached in memory for future interpreter invocations that imported the module. And cached on disk.

  • As a simple function, you can imagine have embellished versions that handled things like version numbering, pre-reqs, etc.

Example

I coded up an implementation of loadModule() for Rhino tonight, along with a simple example that uses four modules:

main.js:


print("loading module: " + __FILE__)

abc = loadModule("abc.js")
def = loadModule("def.js")

abc.sayHello()
def.sayHello()

abc.js:


print("loading module: " + __FILE__)

sayer = loadModule("sayer.js")

function sayHello() {
    sayer.say("hello")
}

def.js:


print("loading module: " + __FILE__)

sayer = loadModule("sayer.js")

function sayHello() {
    sayer.say("world")
}

sayer.js:


print("loading module: " + __FILE__)

function say(message) {
    print(message)
}

Each module prints a line indicating it's being loaded; the environment I set up defines the __FILE__ variable containing the module source file name (my C roots are showing), and a print() function which prints a line to stdout.

main.js loads two modules, abc.js and def.js. It then calls the sayHello() function in the abc module, followed by the sayHello() function in the def module.

abc.js and def.js are identical, except for the message printed from the sayHello() function at the bottom of the file. Both modules load the sayer.js module. They also both define a function with the same name - sayHello() - but that's ok because they live in separate namespaces and can be accessed separately by code that imports them, like main.js does above.

sayer.js defines a say() function prints a string (my REXX roots are showing).

Here's the output of running the main.js module:

loading module: main.js
loading module: abc.js
loading module: sayer.js
loading module: def.js
hello
world

The output shows that the code in sayer.js is only executed once, like with Python modules; subsequent imports just return the module reference which was built during the first import.

The source for the Java code to run this is available here: http://muellerware.org/hg/org.muellerware.jsml/; it's an Eclipse project stored in an hg repository.

There are really only two Java files used for this, if you just want to peek at the code: Main.java and ModuleLoader.java.

Why don't we have something like this in the browser?

Frankly, after spending a very small amount of time implementing the basic functionality, I have to wonder why we don't have something like this in the web browsers today? We have XMLHttpRequest to programmatically fetch data, why don't we have a way of programmatically fetching and executing code? <script src=""> is a sorry excuse of a version of this. Let me code it, dammit!

Tuesday, January 06, 2009

gpx and exif

Using a GPS

We got our first GPS last Christmas, in preparation for our trip to Ireland in the spring (awesome trip, BTW). That was a Garmin nüvi 270, which is the basic hardware device preloaded with maps of the US and Europe. Buying a device without the Europe maps, and then adding them back would have been a little more expensive. The device was quite useful on the trip.

As I'm a man, I've had more need for the device than my wife, and I had been leaving it in my car. So for my birthday this year, my wife got me a basic device, the Garmin nüvi 205. She wanted 'hers' back. When I started hiking a bit more this fall, I took it with me on the hikes, because it sucks to get lost. I could also kinda figure out where I was based on the shape of the track the device was generating, compared to maps showing trails.

The big problem with taking a nüvi hiking is that the battery only lasts 4-5 hours. It never ran out, but came close a few times. Another problem people may have with older devices is that they don't seem to have the nice tracking function that is really what you want in the device, to show you visibly where you've been on the map. Our one year old nüvi 270 doesn't do the tracking thing, near as I can tell. Lastly, it's not terribly convenient to slip into your pocket; it has a very sensitive touch screen and a easily switched on/off switch at the top. I found an old Palm Pilot leather case, with a hard 'front side' to prevent accidental touches through the case, that ended up being a perfect fit (saving $20 or on the Garmin case; I'm a pack-rat), but still a tight fit for the pocket, and you have to slip it in and out the case just so.

For Christmas, my wife ended up getting me a Garmin eTrex Venture HC, which is the basic GPS hiking model. The maps, compared to the nüvi suck, but that's ok, even the default Garmin maps don't include enough detail for hiking. This device handles track data much better than the nüvi, in that you can pre-load a bunch of tracks into the device and then display them while you're hiking. I've got a bit of a long-winded procedure to generate tracks from existing trail maps and Google Earth (see below), which then shows me something close to the actual trails while I'm hiking.

Besides being used for live tracking, the other thing I've been wanting to do is to correlate the pictures I've been taking while hiking with the GPS, so that I can associate a fairly precise location with the pictures. So that's how I spent a bit of my xmas break; writing that program.

What is GPX?

GPX is a XML file that your Garmin device will poop out giving you a braindump of what it knows; "favorites" you've set up, track logs for where you've been, etc. The file format is described pretty well on this site. The only thing I couldn't quickly figure out was the units for the elevation; meters.

The GPX file will contain a list of points, where each point has the following properties - latitude, longitude, elevation, and time - which it collects every so often (you can configure how often this happens). Here's the GPX file from my most recent hike to White Plains Nature Preserve - http://muellerware.org/kml/White-Pines-Nature-Preserve.gpx.

Actually, getting that GPX file can be a little tricky. You'll need to connect your GPS device to your computer, and for Garmin use the software they provide on a CD to pull the GPX file out, or for the Mac use RoadTrip. For RoadTrip, I always create a new folder for each GPX file I want to create, copy just the stuff I want from the "most recent import" (or whatever), and then export that folder, which exports it to a GPX file with the same name as the folder. A bit non-intuitive, but you'll figure it out.

Once you have the GPX file, you can open it directly in Google Earth. Google Maps doesn't appear to directly eat GPX files, but will eat KML files, and you can easily convert a GPX file to a KML file using the gpsbabel program.

What is EXIF?

EXIF is a standard for metadata embedded in image files. The site http://www.exif.org/ explains all, I guess. The spec is a bit dry. All sorts of metadata can get added to images by your camera, including all the camera settings used when the picture was takem, model information, and for this purpose, GPS information.

Here is an example of the sorts of information that gets stored as EXIF data for a photo.

Two great tastes ...

So, now that we have a bunch of images, and a GPX file, it's a SMOP to get the time of the photo, calculate the GPS coordinates given that time, and then stamp them back into the photo.

It looked to be a difficult slog to deal with the EXIF data myself, so some reading quickly led me to the exiftool program which can do all manner of slicing and dicing of EXIF data for your images.

The program I wrote reads in the GPX file, and then for every image pulls out the time the photo was taken with exiftool, and calculates the GPS coordinates for that photo, stamping that data back into the image with exiftool.

The program, gpx2exif, is housed here: http://muellerware.org/hg/gpx2exif/, is written in Python, may required version 2.5 or above, and also requires that you have exiftool installed.

In addition to stamping the images with GPS data (actually, creating new copies of the images with the GPS EXIF data), it also creates a KML file you can load into Google Earth to 'test' the locations that got stamped. In case your camera's clock is not synchronized to the GPS (hint, hint). If your times are off, read the exiftool help, there's a way to adjust the times of your photos in one swell foop.

Once you've got the GPS data stamped in your images, sites like Flickr and Picasa will show you "map" versions of your sets, and do other stuff with the geo data. The map view for my White Pines set at Flickr is here and the map view for the same set at Picasa is here.

What's next

Turns out you can do all sorts of interesting analysis of the data in the GPX file, like:

  • calculate distance travelled
  • calculate speed
  • figure out when you stopped for a break
  • plot data onto Google Maps or Google Earth
  • generate elevation maps

You should be able to do all this stuff in a web browser, in fact, by writing the analysis code in JavaScript. Given that you can access Flickr cross-site via their 'JSONP'-ish support, associating photos with the GPS data is something you can also probably do in the browser. We'll see. I'm a little worried that the number of data points and expensive math required will be a bit much for normal JavaScript processing; I may need to use a Google Gears worker to offload some of that processing.

Notes

  • exiftool rocks; I was was happy to not have to deal with reading/writing EXIF data myself.

  • My camera stores times in "local" format. Would have been nice if it used UTC. Do any cameras do this? I made an assumption that the camera, and computer you are running gpx2exif on, are running at the same local time. Again, use exiftool to "fix" this, if it's wrong.

  • I still can't figure out the secret to the findall() method ElementTree. bugger. Seems like a great API, I just can't use it. The XML processing wasn't that complex, so minidom, which I'm very familiar with, was fine.

  • Neither Flickr nor Picasa will do anything with your EXIF GPS data unless you specifically tell them to; presumably for privacy reasons. For Flickr, the setting is here; for Picasa, the setting is here.

  • The resulting map views from Flickr and Picasa aren't terribly pleasing to me; in fact, the KML "test" file I produce from gpx2exif is way more interesting. I think because you can see the actual trail, but also the markers I used (default ones) work better than thumbnails that Picasa uses, and the markers used by Flickr can't be disambiguated when they're too close, like they can in Google Earth.

  • On the Garmin eTrex device, if you "save" a track that you've made (hiked), it will strip the time values out. Make sure you export the track off the device before saving; the time values are (obviously) critical to determining the locations for your photos.

  • To pre-load a set of trails for a park onto the device, I do the following. Get a version of the trail maps (prolly from a PDF from the park site) and convert to a JPG file. Bring up Google Earth, find the park, and add an image overlay for the image file you created; set the transparency down so you can see the trails and Google Earth detail. Hopefully there's enough detail in the image, and Google Earth so that you can move/resize the image overlay close enough. Then create some new line segments in Google Earth, tracing over the trails. I couldn't figure out how to export those line segments directly out of Google Earth, but if you "mail" the folder they are in to yourself, you will get a KMZ file, which is just a zip file containing a KML file. Garmin tools like RoadMap don't eat KML, but you can convert the KML to a GPX using gpsbabel, and then import that. Voilà; trails to follow on my device.

  • When traveling long distances now, I've become completely dependent on the GPS, and it's very nice to have when you're not on the interstate. In fact, I've been actively avoiding interstates as much as I can now; traveling back roads through small towns is much more fun. You basically don't have to keep track of where you are, what roads you're on, where to turn, etc. As long as you got the destination plugged in right. And then I find myself racing against the ETA the GPS displays prominently. I called my wife at one point when I was coming home from hiking trip and the conversation went something like this:

    wife: So, where are you?
    me: I have no idea.
    wife: Well, which way are you coming home?
    me: I have no idea.
    wife: I don't suppose you know when you'll be getting home?
    me: 4:37