-
Notifications
You must be signed in to change notification settings - Fork 1.2k
High level overview of a compilation job
This document gives a short overview of Closure Compiler's code, and the main classes that are involved in a compilation. It is intended for people who are getting started with the compiler, and want to make changes to it and understand how it works.
For each compilation, an instance of Compiler is created. The compiler class contains several compile
methods, and they all end up calling compileInternal
.
The CommandLineRunner takes the command-line arguments and creates an instance of CompilerOptions. The compiler uses the options object to determine which passes will be run (some checks and some optimizations). This happens in DefaultPassConfig. The two important methods in this class are getChecks
and getOptimizations
.
Before running the checks, the compiler parses the code and creates an abstract-syntax tree (AST). The structure of the AST is described in Node.java and Token.java.
PhaseOptimizer takes the list of passes created in the pass config, and runs them. Running the checks is simple, we just go through the list of checks and run each check once. Some optimization passes run once, and others run in a loop until they can no longer make changes. During an optimization loop, the compiler tries to avoid running passes that are no longer making changes. If you are experimenting with the compiler and want to see code before each pass, add
System.out.println("Starting pass: " + this.name + "\n" + compiler.toSource(root));
at the beginning of the process
method of NamedPass
. If you want to see how long each pass takes, and how each pass changes code size, pass the flag --tracer_mode=ALL
to the compiler.
After all checks and optimizations are finished, the AST is converted back to JavaScript source. See CodeGenerator and CodePrinter.
This is basically it. Below, we briefly describe some of the common compiler passes.
NodeTraversal has utility methods to traverse the AST. All compiler passes use a traversal to go through the code, rather than hand-written recursion.
The type-checking code lives in TypedScopeCreator, TypeInference and TypeCheck. The code for the new type checker (still under development) lives in GlobalTypeInfo and NewTypeInference.
To see some of the optimizations, start at getMainOptimizationLoop and look at the passes used there.