Understanding JavaScript Code
Using
Dynamic Analysis
Understanding JavaScript Code
Using
Dynamic Analysis
Dynamic Analysis Tools
1. Observe Execution
2. Tell You About It
function square(n) {
return n * n
}
square(5)
square("a")
Tern
fn(n: number|string) -> number
Flow
function square(n) {
return n * n
}
square(5)
square("a")
2: return n * n
^ string. The operand of an arithmetic
operation must be a number.
Debugger
Time Travel Debugging
Node-ChakraCore
Console.log
function square(n) {
console.log("n", n)
var ret = n * n
console.log("Return Value", ret)
return ret
}
Automatic Logging
How Can We Collect Runtime Data
Wrapping Functions
window.setTimeout = function() {
console.log("Called setTimeout")
}
Wrapping Functions
var _setTimeout = window.setTimeout
window.setTimeout = function() {
console.log("Called setTimeout")
return _setTimeout.apply(this, arguments)
}
Capture a Call Stack
window.setTimeout = function(){
console.log("Called setTimeout: " + Error().stack)
}
document.createElement = ...
element.innerHTML
...
document.createElement
Object.defineProperty
var _createElement = document.createElement
document.createElement = function(){
var newElement = _createElement.apply(this, arguments)
newElement.__createdAt = new Error().stack
return newElement
}
document.location
4 + 3
person.name = "John"
Wrapping Functions: Limitations
Modifying Code
obj.a = 123
assignPropertyValue(obj, "a", 123)
function assignPropertyValue(object, propertyName, value){
object[propertyName] = value
}
object[propetyName + "__history"] = ...
Babel
transforms js into other js
array.map(n => n * n)
array.map(function (n) {
return n * n;
});
assignPropertyValue(obj, "a", 123)
obj.a = 123
History of an Object
a String
"Hello "
"World"
"Hello World"
"Hello World!"
"!"
var greeting = "Hello " + "World"
greeting += "!"
// "Hello World!"
var hello = "Hello"
var world = "World"
var greeting = hello + world
var hello = stringLiteral("Hello")
// { value: "Hello", stack: "..." }
var world = stringLiteral("World")
// { value: "World", stack: "..." }
var greeting = hello + world
// "[object Object][object Object]"
var hello = stringLiteral("Hello")
var world = stringLiteral("World")
var greeting = add(hello, world)
greeting = {
value: "Hello World",
stack: "(where string concat took place)"
inputs: [
{value: "Hello", stack: "..." },
{value: "World", stack: "..." }
]
}
Problems
High Memory Consumption
Flaky If Invasive Changes
Slow Execution
Capturing Data Directly in the JavaScript Engine
var square;
for (var i=0; i<=1000000; i++) {
log("i", i)
square = i * i
log("square", square)
}
=> Replay execution
var random;
for (var i=0; i<=1000000; i++) {
random = Math.random()
}
=> Capture sources of non-determinism
1. Record execution
2. Replay execution using log
Node-ChakraCore
Collecting Runtime Data
1. Wrap Functions
2. Rewrite Code
3. Integrate with JS Engine
What Can We Do?
Annotate code
Debug a past invocation
Diff two execution traces
...
Node-ChakraCore: Time-Travel debugging for JS apps
Thanks!
Annotate code with runtime values
Track object property assignments
Follow string through application
Demo Links:
Understanding JavaScript Code - EnterJS 2017
By Matt Zeunert
Understanding JavaScript Code - EnterJS 2017
- 1,502