Function-level JavaScript instrumentation with Closure Compiler
29 Sep 2015Overview
This post describes how to do function-level instrumentation of JavaScript programs using a Closure Compiler fork which is available here. The repository contains all code used here in the instrumentation-sample
directory. Program points that can be hooked are function definition, invocation, and exit. Closure supports instrumentation internally as-is, this fork makes it more useful. Since Closure is already a popular part of JS build chains, it was an attractive target to add this feature to. I used this code as part of a project for JS hardening (ZigZag).
Update: The code has been merged into the official Closure repository.
“Hello World”
How to use the instrumentation feature:
$ java -jar compiler.jar --js file.js --instrumentation_template template.txt --formatting pretty_print
instrumentation_template FILE
is the new option. The specified file contains the code that will be added to the JS file.</p>
Code specified as `"init"` will be prepended to the program, this is where function definitions for the report call/exit/defined functions go. The other three types: report_call, report_exit and report_defined specifiy the functions that should be invoked for those actions. These functions are where one wants to fill in the blanks with one's own code to see what's happening in a program.
Here is a bare-bones instrumentation template:
init: "function instr_call(fun_id){}"
init: "function instr_exit(fun_id, return_value){return return_value;}"
init: "function instr_defined(fun_id){}"
report_call: "instr_call"
report_exit: "instr_exit"
report_defined: "instr_defined"
function a(e) {
return e+1;
}
function instr_call(b) {
}
function instr_exit(b, c) {
return c;
}
function instr_defined(b) {
}
instr_defined(0);
function a(b) {
instr_call(0);
return instr_exit(0, b + 1);
}
;
function instr_call(fun_id){
for ( var i = 0; i < arguments.callee.caller.arguments.length; i++) {
console.log('Argument ' + i + ': ' +
arguments.callee.caller.arguments[i]
);
}
}
function instr_exit(fun_id, return_value){return return_value;}
function instr_defined(fun_id){}