removing unused functions from javascript libraries
Since programmers started using JS for writing complex applications, the
need for better tool support during development increased. A common practice in JavaScript development is to ship and deploy an application as a large file, called bundle. This bundle is the result of combining application code with the code of all the libraries the application depends on. Despite the benefits, this approach makes applications being shipped with portions of code that are actually not used. How does it work? A JavaScript (JS) application is commonly deployed by bundling the source code application with the source of the used libraries. The bundling process is making a single file from all JS files called “bundle”. The bundle contains all the application code and the libraries which application uses. For example, when a website has a diagram, it includes a large Diagram.bundle.js file made of the source code of Chart.js and of all its dependent libraries. A library is commonly distributed as a single JS file. They are also available as packages. When using an element from one of libraries in code, functionality is delegated to library functions and it builds dependencies. In typical JS application development, libraries save with the source code. This packaging process is important for reducing a size of application. Download times and the amount of data depend on it. It decreases the memory and power consumption on mobile phones. Functions are a basic unit in JavaScript applications. They are generally contained in .js files. Since JS does not have built-in module mechanisms, JS users community built its own module systems to help developers build small units of independent reusable code at a higher abstraction level than functions. In practice most JavaScript modules are implemented in a separate JS file that can be used by other modules while other functions stay private to the module. Module definitions are necessary to implement many JS design patterns and they are very useful when building applications. Moreover using a module definition, a module can specify dependencies on other modules. This way of packaging applications is comfortable to use. However, this makes it save more code than necessary. This problem is common in the JS community. Some tools like Browserify and Webpack work with analysis techniques that can exclude unreferenced JS modules from a bundle, but applications are still shipped with unused code related to JS libraries. That`s why static analysis is not enough for developers and dynamic analysis should be used too. Authors of the article present the concept of Unused Foreign Function (UFF). It characterizes functions in a JS library used by application, which are not called by application, but are still shipped as part of the application bundle. UFFs are unused functions that do not belong to the application code but to the source code of the dependencies of the application. The fact that these functions are in the source code of dependencies made UFFs difficult to be identified. Authors also created a tool called UFF Remover. It dynamically analyzes the execution to identify and remove unnecessary source code from libraries. It makes the size of bundle distributions smaller. Bundle size can be reduced for 26% on average (comparing to using only static analysis). It works in several steps. UFF Identification process takes three main activities: identification of required modules, instrumentation of required modules and dynamic UFF detection. After the functions are identified, the instrumentation adds an instruction at the beginning of each function defined in a library. This instruction logs information into a file at runtime, recording whether a function was executed. In this way, the output of the activity are all the instrumented functions of the required modules of libraries. It is important that this instrumentation is automatically made by this tool. Next step is dynamic UFF detection. This activity analyzes the information collected at runtime about function executions. For each function in the required modules of the libraries, this activity checks whether a trace exists that indicates that the function was executed. If the function is not found in the trace file it is classified as UFF. In the last step developer chooses which UFFs to delete and the tool helps to remove it. This process is not always easy in real-life applications. The problem can happen with dynamic functions. An example of a dynamic feature is the eval function. Eval is widely used by developers and it can execute code provided as a string, making it a powerful mechanism of reflection. The tool can not understand that application uses this function. However if we remove these functions from the source code, it will crash in the next execution. Developers should pay attention to it using this tool. Tool can be used on open-source application which work in web browsers. Test coverage of the functions must be greater than 85%. It must depend on at least one library. It must have information about how to compile it and run its tests. There were works about understanding and modeling the program dataflow between JS and the host environment such as the browser. Unfortunately, in works that need more precision these studies have difficulties dealing with the dynamic behavior of JS applications. This is the first tool that detects dead code in JS using dynamic analysis. It does not analyze the libraries of an application but the application itself.