Skip to content
This repository has been archived by the owner on Feb 1, 2019. It is now read-only.

JavaScript Tests Module

mattbasta edited this page Oct 29, 2010 · 10 revisions

Hi there! The JS tests bit here is quite a big undertaking and will probably never be 100% reliable or 100% accurate (I would be incredibly surprised if it even got above 90%). Granted, it’s better than using Regexs to do this stuff, it’s much slower, uses a lot more memory, and leaves a bad taste in the mouth of anyone that believes in purity of JavaScript.

Firing it up

Spidermonkey

Getting the Source

The biggest “first thing” to do is to get a build of Spidermonkey running. You can get a build of it through Mercurial. That’s the easiest (and fastest) way to get it together.

mkdir /moz
cd /moz
hg clone http://hg.mozilla.org/mozilla-central/
cd js/src

You can find more here.

Installing the Shell

The second “biggest thing” is making sure you’ve got autoconf-2.13. That specific version. End of story. Anything else won’t work, trust me. If you’re on linux, you can generally plug that into your package manager and it’ll chug away and you’re good to go:

Debian/Ubuntu

apt-get install autoconf-2.13

OS X
It seems that there might be a backport of it. I don’t know. I don’t have a Mac.

Once you have autoconf all good and ready, you’re going to want to build Spidermonkey:

autoconf-2.13
mkdir build-release
cd build-release
../configure
make
cd ..

There’s not too much more to it than that.

Configuring the Validator

The last step (which isn’t bad at all) is to configure the validator to use the installation of Spidermonkey for its business. This is super easy.

If you installed everything line-for-line like I have above, everything is already good. Your Mozilla code should all be in /moz/mozilla-central/. If it is, and you built it line-for-line like I showed you, you’re good as gold. The validator should automatically know where your Spidermonkey installation is. Pat yourself on the back and chalk it up to doing what you’re told.

If you decided to meander your own route through the installation, you’re going to want to learn where you put the binaries for Spidermonkey. In the commands that I listed above, the compiled code ended up in /moz/mozilla-central/js/src/build-release. The shell should be named js. Once you’ve located the shell, put it into the SPIDERMONKEY_INSTALLATION constant in the /validator/constants.py file of your validator installation:

SPIDERMONKEY_INSTALLATION = "/wherever/your/binary/is/js"

And that’s it!

The Problem with AST Trees

The problem with AST trees is that the nodes are not homogenous. For instance, CallExpression nodes will contain more nodes in the “callee” and “arguments” properties, while MemberExpression nodes will contain more nodes in its “object” and “property” nodes.

This problem prevents simple recursion to iterate the tree. Because the nodes are also indicative of the scope of the objects being referred to, it is also difficult to accurately target the use of prohibited objects and functions. I’ll talk about that in a second.

Traversing the trees

So that method for traversing the trees that I’ve come up with boils down the a giant dict in validator.testcases.javascript.nodedefinitions that lists each node type, the branches of that node that can contain other nodes, and some other information about the node type. Based on this, we can lookup each node as it is encountered, and loop through each branch which contains code, recursing into each sub-node.

Doing something useful

Traversing the tree is wonderful, but it doesn’t actually “do” anything. In order to actually test the JS, we need to make some determinations about the code that is being iterated over. In nodedefinitions, there is a value for each node type that identifies whether the object is block-level (so we can track variables created with let) and a value that identifies whether the object declares a scope. These values add a JSContext object to the top of the traverser’s context stack. When an assignment or declaration node is encountered, these contexts are populated.

Some elements, though, are not as simple to figure out as “just traverse and search for bad stuff”. Things like function declarations are especially tricky, where the declaration of the function, assignment of the function to the scope, and the content traversal happen in the same node. In order to handle this, there is a value available in nodedefinitions that allows a lambda function (or reference to a function in validator.testcases.javascript.actions) to run before the branches are executed. For this function, if there is a return value, execution of the branches is skipped. Simply returning True will skip the execution step.

Two arguments are passed to the aforementioned function. The first is a reference to the traverser object. This contains the error bundler (handy for reporting errors) and the AST node.

Lastly, there is a value in the nodedefinitions file for each node type that returns whether the node returns a value. Setting this to true will cause the _traverse_node function to return the value of the lambda function from above.

Clone this wiki locally