How do you do dynamic offline JavaScript anyways?

In a previous post, I established that doing dynamic JavaScript analysis is the way to go for the Titanium Code Processor, but what about the problems inherent in dynamic analysis?

I'll mention again a code snippet from the previous post:

var x = Date.now().getFullYear(),  
    y;
if (x > 2012) {  
    y = 2012;
} else {
    y = x;
}

We don't know what the current date will be when user's actually run the app, so what do we do? My solution is to add a new data type called 'unknown' to the 5 data types already supported by JavaScript (undefined, boolean, number, string, and object). Whenever an expression is encountered that cannot be evaluated, that expression returns 'unknown'.

So what do we do with these values, such as in the code snippet? It depends on the specific JavaScript rule, but the gist is to evaluate all possibilities and keep things generic. In the case of 'if' statements with an unknown conditional, as above, both conditionals are evaluated. "But wait," you say, "you can't do that because JavaScript doesn't have block scope and the order of execution will affect the results." You would be correct of course, so that is where our next bit of magic comes into play.

Whenever a block is evaluated in an unknown state, we evaluate it in 'ambiguous mode.' Ambiguous mode changes the behavior of things like assignment to make sure the results remain consistent. Any assignment in an ambiguous block sets a value of unknown to the identifier, even if we are able to evaluate the value. This way, assignments to the same identifier in both branches of the conditional don't stomp on each other, and we can safely say that 'y' is unknown. Intuitively this makes sense, because we really don't know what the value of y will be. But what if we call a function from within an ambiguous block, like so:

var x = Date.now().getFullYear(),  
    y;
function foo() {  
    var z = 10;
    y = z;
    console.log(x, y, z);
}
if (x < 2012) {  
    foo();
}

We could evaluate the function call the same way we do anything else in an ambiguous block (assignments always assign unknown, etc), but in this case we can be a little more accurate. We do know that the value of z will always be 10, regardless of how the function was called, so we evaluate ambiguous functions as a hybrid of standard evaluation and ambiguous blocks. Anything assigned within the context of a function is evaluated normally, but everything outside the context of the function context is treated as ambiguous. This is applied to nested function calls as well. The ambiguous function forms a sort of context "wall," where everything inside of that function is evaluated in standard mode, but everything outside of the wall is evaluated in ambiguous mode.

These two concepts form the core of the Titanium Code Processor's runtime engine. It's not 100% accurate in all cases of course; contexts can become so full of unknown values that nothing "real" can be evaluated, but it works in most cases (far more cases than static analysis, to be certain). The core concept is quite simple, unknown values and ambiguous blocks and functions, but they are also very powerful and enable us to do offline JavaScript analysis quite effectively. Stay tuned for more posts on unknown values and ambiguous modes.