Fix MimimalRebuildCache leak when compiling multiple modules.

Bug: #9155
Bug-Link: https://github.com/gwtproject/gwt/issues/9155
Change-Id: I324b15c26ceb3dfcff6b1e1bd7aedec3c2be1e94
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
index 3e8c7a0..94c5c60 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/Recompiler.java
@@ -359,7 +359,7 @@
     job.setCompileStrategy(minimalRebuildCache.isPopulated() ? CompileStrategy.INCREMENTAL
         : CompileStrategy.FULL);
 
-    boolean success = new Compiler(runOptions, minimalRebuildCache).run(compileLogger, module);
+    boolean success = Compiler.compile(compileLogger, runOptions, minimalRebuildCache, module);
     if (success) {
       publishedCompileDir = compileDir;
       previousInputSummary = inputSummary;
diff --git a/dev/core/src/com/google/gwt/dev/Compiler.java b/dev/core/src/com/google/gwt/dev/Compiler.java
index bf5b2ab..9496222 100644
--- a/dev/core/src/com/google/gwt/dev/Compiler.java
+++ b/dev/core/src/com/google/gwt/dev/Compiler.java
@@ -46,6 +46,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.FutureTask;
 
@@ -114,7 +115,7 @@
             updater = CheckForUpdates.checkForUpdatesInBackgroundThread(logger,
                 CheckForUpdates.ONE_DAY);
           }
-          boolean success = new Compiler(options).run(logger);
+          boolean success = Compiler.compile(logger, options);
           if (success) {
             CheckForUpdates.logUpdateAvailable(logger, updater);
           }
@@ -129,35 +130,39 @@
     // Exit w/ non-success code.
     System.exit(1);
   }
-  private CompilerContext compilerContext;
-  private final CompilerContext.Builder compilerContextBuilder;
 
-  private final CompilerOptionsImpl options;
-
-  public Compiler(CompilerOptions compilerOptions) {
-    this(compilerOptions, compilerOptions.isIncrementalCompileEnabled() ? new MinimalRebuildCache()
-        : new NullRebuildCache());
-  }
-
-  public Compiler(CompilerOptions compilerOptions, MinimalRebuildCache minimalRebuildCache) {
-    this.options = new CompilerOptionsImpl(compilerOptions);
-    this.compilerContextBuilder = new CompilerContext.Builder();
-    this.compilerContext = compilerContextBuilder.options(options)
-        .minimalRebuildCache(minimalRebuildCache).build();
-  }
-
-  public boolean run(TreeLogger logger) throws UnableToCompleteException {
-    ModuleDef[] modules = new ModuleDef[options.getModuleNames().size()];
-    int i = 0;
-    for (String moduleName : options.getModuleNames()) {
-      ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, moduleName, true);
-      modules[i++] = module;
-    }
-    return run(logger, modules);
-  }
-
-  public boolean run(TreeLogger logger, ModuleDef... modules)
+  public static boolean compile(
+      TreeLogger logger, CompilerOptions compilerOptions)
       throws UnableToCompleteException {
+    List<ModuleDef> moduleDefs = new ArrayList<>();
+    for (String moduleName : compilerOptions.getModuleNames()) {
+      moduleDefs.add(ModuleDefLoader.loadFromClassPath(logger, moduleName, true));
+    }
+
+    boolean result = true;
+    for (ModuleDef moduleDef : moduleDefs) {
+      result &= compile(logger, compilerOptions, moduleDef);
+    }
+    return result;
+  }
+
+  public static boolean compile(
+      TreeLogger logger, CompilerOptions compilerOptions, ModuleDef moduleDef)
+      throws UnableToCompleteException {
+    MinimalRebuildCache minimalRebuildCache = compilerOptions.isIncrementalCompileEnabled()
+        ? new MinimalRebuildCache()
+        : new NullRebuildCache();
+    return compile(logger, compilerOptions, minimalRebuildCache, moduleDef);
+  }
+
+  public static boolean compile(
+      TreeLogger logger,
+      CompilerOptions compilerOptions,
+      MinimalRebuildCache minimalRebuildCache,
+      ModuleDef moduleDef)
+      throws UnableToCompleteException {
+
+    CompilerOptionsImpl  options = new CompilerOptionsImpl(compilerOptions);
     boolean tempWorkDir = false;
     try {
       if (options.getWorkDir() == null) {
@@ -179,69 +184,70 @@
         options.setNamespace(JsNamespaceOption.NONE);
       }
 
-      compilerContext =
-          compilerContextBuilder.unitCache(getOrCreateUnitCache(logger, options)).build();
-
-      for (ModuleDef module : modules) {
-        compilerContext = compilerContextBuilder.module(module).build();
-        String moduleName = module.getCanonicalName();
-        if (options.isValidateOnly()) {
-          if (!Precompile.validate(logger, compilerContext)) {
-            return false;
-          }
-        } else {
-          long beforeCompileMs = System.currentTimeMillis();
-          TreeLogger branch = logger.branch(TreeLogger.INFO,
-              "Compiling module " + moduleName);
-
-          Precompilation precompilation = Precompile.precompile(branch, compilerContext);
-          if (precompilation == null) {
-            return false;
-          }
-          // TODO: move to precompile() after params are refactored
-          if (!options.shouldSaveSource()) {
-            precompilation.removeSourceArtifacts(branch);
-          }
-
-          Event compilePermutationsEvent =
-              SpeedTracerLogger.start(CompilerEventType.COMPILE_PERMUTATIONS);
-          Permutation[] allPerms = precompilation.getPermutations();
-          List<PersistenceBackedObject<PermutationResult>> resultFiles =
-              CompilePerms.makeResultFiles(
-                  options.getCompilerWorkDir(moduleName), allPerms, options);
-          CompilePerms.compile(branch, compilerContext, precompilation, allPerms,
-              options.getLocalWorkers(), resultFiles);
-          compilePermutationsEvent.end();
-
-          ArtifactSet generatedArtifacts = precompilation.getGeneratedArtifacts();
-          PrecompileTaskOptions precompileOptions = precompilation.getUnifiedAst().getOptions();
-
-          precompilation = null; // No longer needed, so save the memory
-          long afterCompileMs = System.currentTimeMillis();
-          double compileSeconds = (afterCompileMs - beforeCompileMs) / 1000d;
-          branch.log(TreeLogger.INFO,
-              String.format("Compilation succeeded -- %.3fs", compileSeconds));
-
-          long beforeLinkMs = System.currentTimeMillis();
-          Event linkEvent = SpeedTracerLogger.start(CompilerEventType.LINK);
-          File absPath = new File(options.getWarDir(), module.getName());
-          absPath = absPath.getAbsoluteFile();
-
-          String logMessage = "Linking into " + absPath;
-          if (options.getExtraDir() != null) {
-            File absExtrasPath = new File(options.getExtraDir(),
-                module.getName());
-            absExtrasPath = absExtrasPath.getAbsoluteFile();
-            logMessage += "; Writing extras to " + absExtrasPath;
-          }
-          Link.link(logger.branch(TreeLogger.TRACE, logMessage), module,
-              module.getPublicResourceOracle(), generatedArtifacts, allPerms, resultFiles,
-              Sets.<PermutationResult>newHashSet(), precompileOptions, options);
-          linkEvent.end();
-          long afterLinkMs = System.currentTimeMillis();
-          double linkSeconds = (afterLinkMs - beforeLinkMs) / 1000d;
-          branch.log(TreeLogger.INFO, String.format("Linking succeeded -- %.3fs", linkSeconds));
+      CompilerContext compilerContext =
+          new CompilerContext.Builder()
+              .options(options)
+              .minimalRebuildCache(minimalRebuildCache)
+              .unitCache(getOrCreateUnitCache(logger, options))
+              .module(moduleDef)
+              .build();
+      String moduleName = moduleDef.getCanonicalName();
+      if (options.isValidateOnly()) {
+        if (!Precompile.validate(logger, compilerContext)) {
+          return false;
         }
+      } else {
+        long beforeCompileMs = System.currentTimeMillis();
+        TreeLogger branch = logger.branch(TreeLogger.INFO,
+            "Compiling module " + moduleName);
+
+        Precompilation precompilation = Precompile.precompile(branch, compilerContext);
+        if (precompilation == null) {
+          return false;
+        }
+        // TODO: move to precompile() after params are refactored
+        if (!options.shouldSaveSource()) {
+          precompilation.removeSourceArtifacts(branch);
+        }
+
+        Event compilePermutationsEvent =
+            SpeedTracerLogger.start(CompilerEventType.COMPILE_PERMUTATIONS);
+        Permutation[] allPerms = precompilation.getPermutations();
+        List<PersistenceBackedObject<PermutationResult>> resultFiles =
+            CompilePerms.makeResultFiles(
+                options.getCompilerWorkDir(moduleName), allPerms, options);
+        CompilePerms.compile(branch, compilerContext, precompilation, allPerms,
+            options.getLocalWorkers(), resultFiles);
+        compilePermutationsEvent.end();
+
+        ArtifactSet generatedArtifacts = precompilation.getGeneratedArtifacts();
+        PrecompileTaskOptions precompileOptions = precompilation.getUnifiedAst().getOptions();
+
+        precompilation = null; // No longer needed, so save the memory
+        long afterCompileMs = System.currentTimeMillis();
+        double compileSeconds = (afterCompileMs - beforeCompileMs) / 1000d;
+        branch.log(TreeLogger.INFO,
+            String.format("Compilation succeeded -- %.3fs", compileSeconds));
+
+        long beforeLinkMs = System.currentTimeMillis();
+        Event linkEvent = SpeedTracerLogger.start(CompilerEventType.LINK);
+        File absPath = new File(options.getWarDir(), moduleDef.getName());
+        absPath = absPath.getAbsoluteFile();
+
+        String logMessage = "Linking into " + absPath;
+        if (options.getExtraDir() != null) {
+          File absExtrasPath = new File(options.getExtraDir(),
+              moduleDef.getName());
+          absExtrasPath = absExtrasPath.getAbsoluteFile();
+          logMessage += "; Writing extras to " + absExtrasPath;
+        }
+        Link.link(logger.branch(TreeLogger.TRACE, logMessage), moduleDef,
+            moduleDef.getPublicResourceOracle(), generatedArtifacts, allPerms, resultFiles,
+            Sets.<PermutationResult>newHashSet(), precompileOptions, options);
+        linkEvent.end();
+        long afterLinkMs = System.currentTimeMillis();
+        double linkSeconds = (afterLinkMs - beforeLinkMs) / 1000d;
+        branch.log(TreeLogger.INFO, String.format("Linking succeeded -- %.3fs", linkSeconds));
       }
 
     } catch (IOException e) {
diff --git a/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java b/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java
index 248ec65..5ee4712 100644
--- a/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java
@@ -23,6 +23,7 @@
 import com.google.gwt.core.ext.soyc.coderef.EntityDescriptorJsonTranslator;
 import com.google.gwt.core.ext.soyc.coderef.EntityRecorder;
 import com.google.gwt.core.ext.soyc.coderef.MethodDescriptor;
+import com.google.gwt.dev.Compiler;
 import com.google.gwt.dev.CompilerOptionsImpl;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
@@ -520,7 +521,7 @@
       options.setExtraDir(new File(work, "extra"));
       PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
       logger.setMaxDetail(TreeLogger.ERROR);
-      new com.google.gwt.dev.Compiler(options).run(logger);
+      Compiler.compile(logger, options);
       // Change parentDir for cached/pre-built reports
       String parentDir = options.getExtraDir() + "/" + benchmark;
       testSymbolMapsCorrespondence(new File(parentDir + "/symbolMaps/"));
diff --git a/dev/core/test/com/google/gwt/core/ext/linker/SymbolMapTest.java b/dev/core/test/com/google/gwt/core/ext/linker/SymbolMapTest.java
index 8b9beb0..a0a774a 100644
--- a/dev/core/test/com/google/gwt/core/ext/linker/SymbolMapTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/linker/SymbolMapTest.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.Compiler;
 import com.google.gwt.dev.CompilerOptionsImpl;
 import com.google.gwt.dev.util.Util;
 import com.google.gwt.dev.util.arg.OptionOptimize;
@@ -217,7 +218,7 @@
       options.setOptimizationLevel(optimizeLevel);
       PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
       logger.setMaxDetail(TreeLogger.ERROR);
-      new com.google.gwt.dev.Compiler(options).run(logger);
+      Compiler.compile(logger, options);
       // Change parentDir for cached/pre-built reports
       String parentDir = options.getExtraDir() + "/" + benchmark;
       for (Map<String, SimpleSymbolData> symbolDataByJsniIdentifier :
diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java
index b41cda7..e6aa553 100644
--- a/dev/core/test/com/google/gwt/dev/CompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java
@@ -19,6 +19,7 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.UnstableNestedAnonymousGenerator.OutputVersion;
 import com.google.gwt.dev.cfg.EntryMethodHolderGenerator;
+import com.google.gwt.dev.cfg.ModuleDef;
 import com.google.gwt.dev.cfg.ModuleDefLoader;
 import com.google.gwt.dev.cfg.ResourceLoader;
 import com.google.gwt.dev.cfg.ResourceLoaders;
@@ -2341,7 +2342,7 @@
       options.setExtraDir(new File(compileWorkDir, "extra"));
 
       // Run the compiler once here.
-      new Compiler(options).run(logger);
+      Compiler.compile(logger, options);
     } finally {
       Util.recursiveDelete(compileWorkDir, false);
       if (oldPersistentUnitCacheValue == null) {
@@ -2369,14 +2370,14 @@
       TreeLogger logger = TreeLogger.NULL;
 
       // Run the compiler once here.
-      new Compiler(options).run(logger);
+      Compiler.compile(logger, options);
       Set<String> firstTimeOutput =
           Sets.newHashSet(new File(options.getWarDir() + "/hello").list());
 
       options.setWarDir(new File(secondCompileWorkDir, "war"));
       options.setExtraDir(new File(secondCompileWorkDir, "extra"));
       // Run the compiler for a second time here.
-      new Compiler(options).run(logger);
+      Compiler.compile(logger, options);
       Set<String> secondTimeOutput =
           Sets.newHashSet(new File(options.getWarDir() + "/hello").list());
 
@@ -2520,11 +2521,10 @@
     // Cause the module to be cached with a reference to the prefixed resource loader so that the
     // compile process will see those resources.
     ModuleDefLoader.clearModuleCache();
-    ModuleDefLoader.loadFromResources(logger, moduleName, resourceLoader, true);
+    ModuleDef moduleDef = ModuleDefLoader.loadFromResources(logger, moduleName, resourceLoader, true);
 
     // Run the compile.
-    Compiler compiler = new Compiler(compilerOptions, minimalRebuildCache);
-    compiler.run(logger);
+    Compiler.compile(logger, compilerOptions, minimalRebuildCache, moduleDef);
 
     // Find, read and return the created JS.
     File outputJsFile = null;
diff --git a/dev/core/test/com/google/gwt/dev/SoycTest.java b/dev/core/test/com/google/gwt/dev/SoycTest.java
index 382008f..18afe86 100644
--- a/dev/core/test/com/google/gwt/dev/SoycTest.java
+++ b/dev/core/test/com/google/gwt/dev/SoycTest.java
@@ -42,7 +42,7 @@
       options.setExtraDir(new File(work, "extra"));
       PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
       logger.setMaxDetail(TreeLogger.ERROR);
-      new Compiler(options).run(logger);
+      Compiler.compile(logger, options);
 
       // make sure the files have been produced
       assertTrue(new File(options.getExtraDir() + "/hello/soycReport/compile-report/index.html").exists());
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java
index e4f1653..3cbbc4b 100644
--- a/user/src/com/google/gwt/junit/JUnitShell.java
+++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -1089,7 +1089,7 @@
 
     boolean success = false;
     try {
-      success = new Compiler(options).run(getTopLogger(), module);
+      success = Compiler.compile(getTopLogger(), options, module);
     } catch (Exception e) {
       getTopLogger().log(Type.ERROR, "Compiler aborted with an exception ", e);
     }