Monday, November 23, 2009

Practical Chickenfoot Presentation

Today I gave a guest lecture on Chickenfoot to Monzy's course on mashups at NYU's Tisch School of the Arts. The presentation is available online as a Google Doc. Unlike previous talks on Chickenfoot, this one focused on practical examples of how to use it rather than the research behind it. This was a nice change because I actually got to sit down with a classroom full of first-time users of Chickenfoot as they went through the exercises. Thanks to Monzy and his students at ITP for having me!

Wednesday, November 18, 2009

Example of using Closure Compiler to attain an 85% improvement over YUI Compressor when minifying Closure Library code

For those of you who do not care about caveats or grains of salt: here is my test showing Closure Compiler beating YUI Compressor by 85% when compiling a simple JavaScript file that uses a widget from the Closure Library.

I chose the title of this blog post very carefully: I do not claim that the Closure Compiler will always minify 85% better than YUI Compressor, and I wanted to make it clear that the dramatic results are specific to Closure Library code which is written in a style that was designed to be minified by the Closure Compiler. Although both tools can be used independently, they were meant to be used together.

That is not to say that other libraries cannot get dramatic minification benefits from the Compiler -- they can! But doing so requires following the style guidelines explained in the Advanced Compilation and Externs article on the Google Code web site.

And even if you think my test is unfair, or that I rigged it to butcher the YUI Compressor, I hope you can at least benefit from these two code samples that came out of creating these tests:

Monday, November 16, 2009

Updates to Externs File for Maps V2 API

Kai found some missing items in my Maps V2 externs file. I was missing methods from the following classes: GControl, GMapType, GMarker, GProjection, and GTileLayer. I also apparently missed GPolyline.fromEncoded() and GPolygon.fromEncoded() because they were listed under Factory Methods, which my script did not know to look for.

Apparently Kai has also found an inconsistency between the online documentation for GPolyline.fromEncoded() and its implementation, but I want to get someone from the Maps team to confirm before updating that.

Wednesday, November 11, 2009

Externs file for the Google Maps API V2

If you're using the Closure Compiler with ADVANCED_OPTIMIZATIONS, then you need to make sure that you are declaring your externs appropriately. The Compiler includes many common externs by default, such as document and window, but externs files for Google JavaScript libraries do not appear to be available yet.

Using Chickenfoot, I generated an externs file for the Google Maps API V2. I strongly encourage you to use it when compiling JavaScript that uses Google Maps! If you find that the externs file has some mistakes, please fix the Chickenfoot script rather than the externs file itself. If you look carefully at the script, I had to add a number of hacks to work around bugs in Google's online HTML documentation, so there could be others that I have missed!

Friday, November 6, 2009

The Closure Compiler turns a Pattern into an Antipattern

I have been waiting months to publish this blog post. When I attended The Ajax Experience 2009, I saw a number of "misguided" things that people were doing with JavaScript.* Knowing that Google was in the process of open-sourcing the Closure Compiler and the Closure Library (I was working on the Compiler effort before I left), I wanted to get up and yell, "Stop -- you're all doing it wrong!"

But I didn't.

The Closure Library and Closure Compiler that Google released yesterday are game-changing. I have always heavily subscribed to Fred Brooks's No Silver Bullet argument, but it's possible that the Compiler will improve your web development processes by an order of magnitude. Errors that previously may not have been caught until manual testing can now be caught at compile time. Before the Closure Compiler came along, there wasn't even such a thing as "compile time" when it came to JavaScript!

The Compiler is complex -- it will likely take some time for developers to wrap their heads around it. It was hard to write JavaScript at Google without also thinking about what the Compiler would do to it. For sure, the Compiler will change the way you write JavaScript code.

To illustrate one of the things that will change, I put together a detailed article on inheritance patterns in JavaScript that compares the functional and pseudoclassical object creation patterns. Now that the Closure Compiler is available, I expect the pseudoclassical pattern (that the Closure Library uses) to dominate.

Special thanks to Mihai and Kushal for reading drafts of the inheritance article.

*I saw a number of innovative things at the conference as well, but none that would have the same impact as the Closure Compiler. However, there was one presentation at The Ajax Experience titled The Challenges and Rewards of Writing a 100K line JavaScript Application that revealed that a company called Xopus in the Netherlands had built their own JavaScript compiler. It is the closest thing I have seen outside of Google to the Closure Compiler. My only objection to it is that I believe that the input to their compiler is not executable on its own.

For example, slide 12 shows some sample code that calls Extends("com.xopus.code.Animal") which has no apparent reference to the class doing the extending, so it is hard to imagine how calling Extends() has the side-effect of providing Animal's methods to Monkey. Presumably, the compiler treats Extends() as a preprocessor directive whereas Closure's goog.inherits() will actually add the superclass's methods to the subclass's prototype. Closure generally relies on annotations, such as @extends, for compiler directives rather than function calls. Closure code should always be executable in both raw and compiled modes.

Calling the Closure Compiler from Java

Someone asked how to deal with Compiler input and output as strings on the closure-compiler-discuss Google group, so here is a code snippet that should help out:

  /**
* @param code JavaScript source code to compile.
* @return The compiled version of the code.
*/
public static String compile(String code) {
Compiler compiler = new Compiler();

CompilerOptions options = new CompilerOptions();
// Advanced mode is used here, but additional options could be set, too.
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(
options);

// To get the complete set of externs, the logic in
// CompilerRunner.getDefaultExterns() should be used here.
JSSourceFile extern = JSSourceFile.fromCode("externs.js",
"function alert(x) {}");

// The dummy input name "input.js" is used here so that any warnings or
// errors will cite line numbers in terms of input.js.
JSSourceFile input = JSSourceFile.fromCode("input.js", code);

// compile() returns a Result, but it is not needed here.
compiler.compile(extern, input, options);

// The compiler is responsible for generating the compiled code; it is not
// accessible via the Result.
return compiler.toSource();
}

This is packaged in a runnable example in CallCompilerProgrammaticallyExample.java.

Note that the recommended way of extending compiler behavior is to subclass CompilerRunner (most likely createOptions() will be the method you are interested in overriding). Your subclass should have a main() method and be set as the Main-Class in a jar. Such a jar can then be used with the -c option in the calcdeps.py utility.

Thursday, November 5, 2009

Google releases Closure Tools!

Today Google released a suite of JavaScript tools that are used to construct massive web applications such as Gmail and Google Docs. The suite is named Closure Tools, and I think you'll find it superior to existing offerings, particularly with respect to minifying JavaScript. The post on the Google Code blog links to a ton of new code and documentation, so trying to determine where to start can be overwhelming.

I used all of these tools when I worked at Google, so there is a lot I could write about them, but for now I'm just going to try to convince you that the Closure Compiler is not your ordinary JavaScript minifier. There is a web version of the Compiler that you can play with to convince yourself that it is worth learning more about. If you go to the site, you will see the following code:

// ADD YOUR CODE HERE
function hello(name) {
alert('Hello, ' + name);
}
hello('New user');

Using the Compile button to run the Compiler, try each of the options to the right of the Optimization: label when compiling the sample code. The compiled code appears in the first tab on the right side of the screen. Note that running with the Advanced option, the code is compiled down to:

alert("Hello, New user");

Now that's some serious inlining! Can your minifier do that? Probably not. To get the maximum benefit out of Closure Tools, you should fix your JavaScript code so that it compiles in Advanced mode. Reading Google's article on advanced compilation is the quickest way to learn what the Compiler expects from your JavaScript, and it should also give you an idea of what other optimizations the Compiler is capable of.

Wednesday, November 4, 2009

NES Emulators and Applescript

One of the things I planned to do after leaving Google was port an NES emulator written in Java to JavaScript. With a JavaScript emulator, it would be possible to play Nintendo games on an iPhone via a web page. (The alternative would be to write an emulator in Objective-C and package it as an iPhone app, but that would be unlikely to make it through Apple's approval process.)

Fortunately, before I sat down to start coding, I did a quick Google search to see whether someone had already done this. In turns out that a few weeks earlier, a student named Ben Firshman released JSNES, a JavaScript NES emulator!

One of the most striking things about JSNES is that it runs at full speed in Google Chrome, but barely runs on Firefox 3.5 or Safari 4. This makes me think we've been going about browser benchmarks all wrong -- I don't care which one can calculate the nth Fibonacci number the fastest, I just care about which one lets me play Contra! (Though to be fair, this probably has more to do with performance differences with a browser's <canvas> implementation rather than its JavaScript engine.)

So there I was, all ready to write all this JavaScript code to find out that it had already been done. Duplicating it seemed like a bad idea, but I still had the NES on my mind, so I started looking around to see what other advances in emulation had come along since I last played around with it a couple of years ago.

I knew Craig had bought a kit some years back which, after some soldering, made it possible for him to plug a real NES controller into a USB port. Fortunately, technology has improved and now you can buy
an adapter with an NES port on one end and a USB port on the other. No soldering required! Since the "USB NES RetroPort" costs $19 and a used NES controller only costs $3, it seemed like a good idea to buy a couple of RetroPorts and a stack of controllers so I can just swap in new controllers when the buttons wear out.

I bought some hardware (the guy who runs retrousb.com was very helpful) and started playing with different emulators to see which ones would support my new controllers. I learned that many emulators do not let you configure your joystick directly; instead, you are expected to install JoyToKey to convert joystick input to keyboard input and then map your joystick to the key commands required for your emulator. Honestly, it worked fine, but I wanted an all-in-one solution.

On Windows, VirtuaNES worked quite well and had support for configuring controllers, but the web site was in Japanese, so it took me awhile to figure out how to do that. Once I confirmed that my controllers were working, I started looking at emulators for Mac because I wanted to rekindle my project from over two years ago of using a PowerPC Mac Mini as my NES emulation hub.

Emulator options are much more limited on Mac. I started out by looking for emulators written in Java, since those should be cross-platform. I took another look at NESCafe (which I reported did not support sound in 2007, but seems to now), but as far as I could tell, it only supported one controller, so that was a deal-breaker. Then I took a look at vNES, which seemed much more promising, except for this FAQ that claimed for reasons unknown, it would not work on a PowerPC Mac.

However, the code for vNES is open sourced under the GPL 3, so I thought I would take a stab at it. The bug turned out to be very simple (it seems like the type of thing that FindBugs should be able to pick out easily):

if(mixerInfo==null || mixerInfo.length==0){
//System.out.println("No audio mixer available, sound disabled.");
Globals.enableSound = false;
return;
}

mixer = AudioSystem.getMixer(mixerInfo[1]);

If you look carefully, you'll see that although there is a check to determine whether mixerInfo is empty, it uses mixerInfo[1] for no documented reason. When I looked at the mixerInfo array, I discovered it only had one value on my PowerPC Mac Mini, two values on my Intel Mac Mini, and nine values on my Vista Thinkpad! I changed the code to use mixerInfo[0] and all was well.

(Aside: Why is the code for every emulator I look at so messy? There are never any comments -- it makes me think one guy figured out how the NES worked and everyone else has just cloned it, so there aren't any comments because no one really knows what is going on. Also, all the classes are in the default package, there are println statements commented out all over the place, etc. In debugging vNES, I tried to clean things up a bit by putting the code in a com.virtualnes package, adding a build.xml file, and refactoring things so it could be run as either an application or an applet. I am making the zip with my changes to vNES 2.11 available on bolinfest.com. I would have tried to contribute a patch to vNES, but the Google Code project appears to be empty.)

Although I got vNES working on my PPC Mini, it was prohibitively slow. Since I had already been playing around with the source code, I considered trying to optimize it, but because of the "no comments in the source code" thing, I realized that could take days. Instead, I went back to Nestopia.

Richard Bannister's Nestopia is a solid emulator for the Mac. It runs at full speed on the PPC and looks great when output to my flatscreen TV. The sound works, both of my controllers hooked up via my RetroPorts work -- this is the real deal.

The only thing it doesn't do is take the path to the ROM as a command-line option, and this is what kept me up past 4am last night. You see, I want to build a Cover Flow UI on top of the emulator for selecting the game to play. To do that, I need to be able to programmatically open Nestopia with a particular ROM file.

If Nestopia were open source, I could have tried to fix it myself to support this feature. In the release notes bundled with Nestopia 1.4.1, the author notes
Martin Freij has generously agreed to license Nestopia to me under a closed-source license for the present. As soon as I have my API kit ready, a buildable version of Nestopia will be released with my shell library. The license for this has yet to be decided but most likely will be normal GPL with my shell excluded under section three of the license. This has been postponed repeatedly due to lack of time but will be released one day - honest!
That dates back to September 27, 2008, so I wasn't going to hold my breath waiting for the source to be released. Besides, from his list of projects, Richard seems to have a lot going on, so I can imagine that he doesn't have the time for this sort of thing.

Regardless, I want my Cover Flow! Because I couldn't change the code for Nestopia, I tried to automate it with AppleScript instead. This is when I should have put the coffee on. According to Google Web History, I did over 100 searches last night while developing my script.

The first thing I that I got to work (after much experimentation) was the following:

on run argv
tell application "Nestopia"
quit
end tell

tell application "Nestopia"
activate
delay 1
end tell

tell application "System Events"
tell process "Nestopia"
-- Click the "Maybe later" button when asked to register.
click at {700, 350}

delay 1
-- Cancel the "Quick Start" mode using the Escape key.
-- It's possible to disable Quick Start in Preferences,
-- but it's useful to have when not using this script.
key code 53

-- Hit command+shift+G to get the "Open Folder" dialog.
delay 1
keystroke "g" using {command down, shift down}

-- because the "Open Folder" dialog only deals with
-- folders and not files, we put each .nes file in
-- its own folder so we can open the folder and
-- then reliably select the only item that comes up
delay 1
keystroke (item 1 of argv)
key code 36

-- use the down arrow to select the file and hit enter
delay 1
key code 125
key code 36

-- go into fullscreen mode using the keyboard shortcut
keystroke "`" using {command down}
end tell
end tell
end run

The part of this script that is particularly gross is the logic with the "Open Folder" dialog. Nestopia displays what appears to be a standard "File Open" dialog, but I could not, for the life of me, figure out how to script it. As you can see, I resorted to using key and mouse events to type in the value I wanted, and had I relied on this, I would have had to have an individual folder for each ROM because Finder (at least on 10.4.11, which is what my PPC Mini runs) lets you type in folder names, but not path names. If there are any AppleScript masters out there, I'd be very curious to see how else you would do this.

Unfortunately, it was not until hours after I started this project that I rediscovered the code I wrote in 2007. Back then, I wrote a CGI script in Perl which would build up some AppleScript and run it from the command line. At the time, this was the easiest way to send a command via HTTP to my Mini to kick off Nestopia:

#!/usr/bin/perl
use CGI qw(param);

# let's get this out of the way before we forget!
print "Content-type: text/html\n\n";

my $rom = param("rom");

# "/Library/WebServer/Documents/nintendo/roms/"
my $folder = 'of folder "roms" ' .
'of folder "nintendo" ' .
'of folder "Documents" ' .
'of folder "WebServer" ' .
'of folder "Library" ' .
'of startup disk';

my $cmd = "osascript -e 'tell application \"Finder\"' " .
" -e 'open file \"$rom\" $folder' " .
" -e 'end tell' ";

system($cmd);
print $cmd;

One thing that you'll notice is that file paths in Finder are gross. I ended up doing the of folder thing because that was the code Script Editor produced when I used Record to help figure out the AppleScript I needed to write. The one good thing about this script, however, was that it reminded me that simply opening the file would trigger Nestopia because it is the application associated with ROM files on my Mac. This helped me clean up my current script considerably:

on run argv
tell application "Finder" to open file ((POSIX file (item 1 of argv)) as string)

delay 1

tell application "Nestopia" to activate

-- Make sure "Enable access for assistive devices" is enabled in
-- Universal Access under System Preferences or else
-- System Events won't work:
-- http://dougscripts.com/itunes/itinfo/keycodes.php
tell application "System Events"
tell process "Nestopia"
-- click the "Maybe later" button when asked to register
click at {700, 350}

-- go into fullscreen mode using the keyboard shortcut
delay 1
keystroke "`" using {command down}
end tell
end tell
end run

It took me at least half an hour of Googling until I came across a solution for passing in the file path as an argument. Apparently AppleScript only deals with HFS paths instead of POSIX paths like everyone else. It is particularly frustrating that Script Editor allows you to write POSIX file "/Users/bolinfest/drmario.nes", but as soon as you compile the code, it becomes file "Macintosh HD:Users:bolinfest:drmario.nes". What kind of editor rewrites your code into some kind of unmaintainable equivalent when you compile it?

I'm exhausted, so I haven't even started working on the Cover Flow part of the project yet, but at least I've resolved one of the big issues. It looks like there are working examples of Cover Flow UIs in JavaScript, so I will likely set up a web server on my Mac Mini with a similar CGI script that will shell out to my compiled AppleScript to launch the ROM. That way, I'll be able to browse my NES catalog from Safari on my iPhone and kick things off from there!

Monday, November 2, 2009

Functional Testing for Web Applications

I put together an article on functional testing for web applications. In it, I argue that a solution for performing functional testing on modern web applications should have the following properties:

  1. User input should be able to be simulated using native key and mouse events.
  2. Tests should be able to be written in JavaScript.
  3. Test code should be able to suspend execution while waiting for network
    or other events.
  4. Tests should be able to run in multiple browsers and on multiple platforms.
  5. Tests should not hardcode XPaths, element ids, or CSS class names.
  6. Test suites should be able to be run via a cron job.

I discuss the properties in detail, give an example of a system that exhibits most (but not all) of the properties, and then ask browser vendors to come in and save the day.