Content area
Static analysis of JavaScript remains a notoriously difficult problem due to the language’s highly dynamic and reflective nature. Features such as dynamic property accesses, higher-order functions, prototype-based inheritance, and event-driven control flow introduce fundamental obstacles to sound and scalable analysis. A central challenge is the construction of precise call graphs, which serve as a critical prerequisite for most interprocedural analyses. Imprecise call graphs often result in high false-positive rates, undermining the effectiveness of downstream analyses and increasing the manual burden on developers. This thesis addresses core limitations in JavaScript static analysis. The first work presents an empirical study of call graph unsoundness in real-world JavaScript applications. We systematically identify and categorize the root causes of missing call graph edges in state-of-the-art call graph construction techniques, such as dynamic property accesses, higher-order functions, and indirect call patterns. This analysis reveals fundamental weaknesses in existing techniques and provides actionable insights for improving call graph recall. The second work introduces an indirection-bounded call graph construction technique. Motivated by the observation that real-world JavaScript code typically exhibits shallow levels of indirection in data flows, the technique bounds the depth of object and function dereferencing to reduce analysis overhead. This approach achieves up to a 2X speedup on large Node.js applications while maintaining nearly identical precision and incurring a modest reduction in recall, making whole-program analysis more tractable at scale. The final work focuses on statically detecting promise misuses, a class of bugs that arise from JavaScript’s asynchronous programming model through PromisePatrol–an abstract interpretation framework that models promise creation, state transitions, and handler attachment interprocedurally. The analysis identifies missing fulfillment and rejection handlers that may result in silent loss of asynchronous results or unhandled rejections. Applied to 100 popular npm packages, PromisePatrol uncovers 15 previously undocumented instances of missing handlers across 7 projects, demonstrating the practical utility of the approach in identifying real-world asynchronous defects. Together, these techniques advance the state of JavaScript static analysis by improving the soundness of call graph construction, enabling scalable interprocedural reasoning, and providing effective tools for detecting asynchronous programming errors in complex, real-world codebases.