Abstract.
JavaScript is the main scripting language for Web browsers, and it is essential to modern Web
applications. Programmers have started using it for writing complex applications, but there is still little
tool support available during development. We present a static program analysis infrastructure that can
infer detailed and sound type information for JavaScript programs using abstract interpretation. The
analysis is designed to support the full language as defined in the ECMAScript standard, including its
peculiar object model and all built-in functions. The analysis results can be used to detect common
programming errors – or rather, prove their absence, and for producing type information for program
comprehension. Preliminary experiments conducted on real-life JavaScript code indicate that the
approach is promising regarding analysis precision on small and medium size programs, which constitute
the majority of JavaScript applications. With potential for further improvement, we propose the analysis
as a foundation for building tools that can aid JavaScript programmers. 1 Introduction In 1995, Netscape
announced JavaScript as an “easy-to-use object scripting language designed for creating live online
applications that link together objects and resources on both clients and servers” [25]. Since then, it has
become the de facto standard for client-side scripting in Web browsers but many other applications also
include a JavaScript engine. This prevalence has lead developers to write large programs in a language
which has been conceived for scripting, but not for programming in the large. Hence, tool support is
badly needed to help debug and maintain these programs. The development of sound programming
tools that go beyond checking mere syntactic properties requires some sort of program analysis. In
particular, type analysis is crucial to catch representation errors, which e.g. confuse numbers with
programmer because it rules out this class of programming errors entirely. ⋆ Supported by The Danish
strings or booleans with functions, early in the development process. Type analysis is a valuable tool to a
Research Council for Technology and Production, grant no. 274-07-0488. †Corresponding author.
Applying type analysis to JavaScript is a subtle business because, like most other scripting languages,
JavaScript has a weak, dynamic typing discipline which resolves many representation mismatches by
silent type conversions. As JavaScript supports objects, first-class functions, and exceptions, tracking the
flow of data and control is nontrivial. Moreover, JavaScript’s peculiarities present a number of
challenges that set it apart from most other programming languages: – JavaScript is an object-based
language that uses prototype objects to model inheritance. As virtually all predefined operations are
accessed via prototype objects, it is imperative that the analysis models these objects precisely. –
Objects are mappings from strings (property names) to values. In general, properties can be added and
removed during execution and property names may be dynamically computed. – Undefined results,
such as accessing a non-existing property of an object, are represented by a particular value undefined,
but there is a subtle distinction between an object that lacks a property and an object that has the
property set to undefined. – Values are freely converted from one type to another type with few
exceptions. In fact, there are only a few cases where no automatic conversion applies: the values null
and undefined cannot be converted to objects and only function values can be invoked as functions.
Some of the automatic conversions are non-intuitive and programmers should be aware of them. – The
language distinguishes primitive values and wrapped primitive values, which behave subtly different in
certain circumstances. – Variables can be created by simple assignments without explicit declarations,
but an attempt to read an absent variable results in a runtime error. JavaScript’s with statement breaks
ordinary lexical scoping rules, so even resolving variable names is a nontrivial task. – Object properties
can have attributes, like ReadOnly. These attributes cannot be changed by programs but they must be
taken into account by the analysis to maintain soundness and precision. – Functions can be created and
called with variable numbers of parameters. – Function objects serve as first-class functions, methods,
and constructors with subtly different behavior. An analysis must keep these uses apart and detect
initialization patterns. – With the eval function, a dynamically constructed string can be interpreted as a
program fragment and executed in the current scope. – The language includes features that prescribe
certain structures (the global object, activation objects, argument objects) in the implementation of the
runtime system. These structures must be modeled in an analysis to obtain sufficient precision. This
paper reports on the design and implementation of a program analyzer for the full JavaScript language.
In principle, the design is an application of abstract interpretation using the monotone framework [9,
21]. However, the challenges explained above result in a complicated lattice structure that forms the
basis of our analysis. Starting from a simple type lattice, the lattice has 2 evolved in a number of steps
driven by an observed lack of precision on small test cases. As the lattice includes precise singleton
values, the analyzer duplicates a large amount of the functionality of a JavaScript interpreter including
the implementation of predefined functions. Operating efficiently on the elements of the lattice is
another non-trivial challenge. The analyzer is targeted at hand-written programs consisting of a few
thousand lines of code. We conjecture that most existing JavaScript programs fit into this category. One
key requirement of the analysis is soundness. Although several recent bug finding tools for other
languages sacrifice soundness to obtain fewer false positives [5, 12], soundness enables our analysis to
guarantee the absence of certain errors. Moreover, the analysis is fully automatic. It neither requires
program annotations nor formal specifications. While some programming errors result in exceptions
being thrown, other errors are masked by dynamic type conversion and undefined values. Some of
these conversions appear unintuitive in isolation but make sense in certain circumstances and some
programmers may deliberately exploit such behavior, so there is no clear-cut definition of what
constitutes an “error”. Nevertheless, we choose to draw the programmer’s attention to such potential
errors. These