/**
  * Create a compilation unit that can be serialized from another {@link CompilationUnit}.
  *
  * @param unit A unit to copy
  * @param sourceToken A valid {@DiskCache} token for this unit's source code.
  * @param astToken A valid {@DiskCache} token for this unit's serialized AST types.
  */
 @SuppressWarnings("deprecation")
 CachedCompilationUnit(CompilationUnit unit, long sourceToken, long astToken) {
   assert unit != null;
   this.compiledClasses = unit.getCompiledClasses();
   this.contentId = unit.getContentId();
   this.dependencies = unit.getDependencies();
   this.resourceLocation = unit.getResourceLocation();
   this.resourcePath = unit.getResourcePath();
   this.jsniMethods = unit.getJsniMethods();
   this.lastModified = unit.getLastModified();
   this.methodArgNamesLookup = unit.getMethodArgs();
   this.typeName = unit.getTypeName();
   this.isError = unit.isError();
   this.isGenerated = unit.isGenerated();
   this.isSuperSource = unit.isSuperSource();
   CategorizedProblem[] problemsIn = unit.getProblems();
   if (problemsIn == null) {
     this.problems = null;
   } else {
     this.problems = new CategorizedProblem[problemsIn.length];
     for (int i = 0; i < problemsIn.length; i++) {
       this.problems[i] = new SerializableCategorizedProblem(problemsIn[i]);
     }
   }
   this.astToken = new DiskCacheToken(astToken);
   this.astVersion = GwtAstBuilder.getSerializationVersion();
   this.sourceToken = new DiskCacheToken(sourceToken);
 }
Пример #2
0
    /**
     * Compiles the source code in each supplied CompilationUnitBuilder into a CompilationUnit and
     * reports errors.
     *
     * <p>A compilation unit is considered invalid if any of its dependencies (recursively) isn't
     * being compiled and isn't in allValidClasses, or if it has a signature that doesn't match a
     * dependency. Valid compilation units will be added to cachedUnits and the unit cache, and
     * their types will be added to allValidClasses. Invalid compilation units will be removed.
     *
     * <p>I/O: serializes the AST of each Java type to DiskCache. (This happens even if the
     * compilation unit is later dropped.) If we're using the persistent unit cache, each valid unit
     * will also be serialized to the gwt-unitcache file. (As a result, each AST will be copied
     * there from the DiskCache.) A new persistent unit cache file will be created each time
     * compile() is called (if there's at least one valid unit) and the entire cache will be
     * rewritten to disk every {@link PersistentUnitCache#CACHE_FILE_THRESHOLD} files.
     *
     * <p>This function won't report errors in invalid source files unless suppressErrors is false.
     * Instead, a summary giving the number of invalid files will be logged.
     *
     * <p>If the JDT compiler aborts, logs an error and throws UnableToCompleteException. (This
     * doesn't happen for normal compile errors.)
     */
    Collection<CompilationUnit> compile(
        TreeLogger logger,
        CompilerContext compilerContext,
        Collection<CompilationUnitBuilder> builders,
        Map<CompilationUnitBuilder, CompilationUnit> cachedUnits,
        EventType eventType)
        throws UnableToCompleteException {
      UnitCache unitCache = compilerContext.getUnitCache();
      // Initialize the set of valid classes to the initially cached units.
      for (CompilationUnit unit : cachedUnits.values()) {
        for (CompiledClass cc : unit.getCompiledClasses()) {
          // Map by source name.
          String sourceName = cc.getSourceName();
          allValidClasses.put(sourceName, cc);
        }
      }

      ArrayList<CompilationUnit> resultUnits = new ArrayList<CompilationUnit>();
      do {
        final TreeLogger branch = logger.branch(TreeLogger.TRACE, "Compiling...");
        // Compile anything that needs to be compiled.
        buildQueue = new LinkedBlockingQueue<CompilationUnitBuilder>();
        final ArrayList<CompilationUnit> newlyBuiltUnits = new ArrayList<CompilationUnit>();
        final CompilationUnitBuilder sentinel = CompilationUnitBuilder.create((GeneratedUnit) null);
        final Throwable[] workerException = new Throwable[1];
        final ProgressLogger progressLogger =
            new ProgressLogger(branch, TreeLogger.TRACE, builders.size(), 10);
        Thread buildThread =
            new Thread() {
              @Override
              public void run() {
                int processedCompilationUnitBuilders = 0;
                try {
                  do {
                    CompilationUnitBuilder builder = buildQueue.take();
                    if (!progressLogger.isTimerStarted()) {
                      // Set start time here, after first job has arrived, since it can take a
                      // little
                      // while for the first job to arrive, and this helps with the accuracy of the
                      // estimated times.
                      progressLogger.startTimer();
                    }
                    if (builder == sentinel) {
                      return;
                    }
                    // Expensive, must serialize GWT AST types to bytes.
                    CompilationUnit unit = builder.build();
                    newlyBuiltUnits.add(unit);

                    processedCompilationUnitBuilders++;
                    progressLogger.updateProgress(processedCompilationUnitBuilders);
                  } while (true);
                } catch (Throwable e) {
                  workerException[0] = e;
                }
              }
            };
        buildThread.setName("CompilationUnitBuilder");
        buildThread.start();
        Event jdtCompilerEvent = SpeedTracerLogger.start(eventType);
        long compilationStartNanos = System.nanoTime();
        try {
          compiler.doCompile(branch, builders);
        } finally {
          jdtCompilerEvent.end();
        }
        buildQueue.add(sentinel);
        try {
          buildThread.join();
          long compilationNanos = System.nanoTime() - compilationStartNanos;
          // Convert nanos to seconds.
          double compilationSeconds = compilationNanos / (double) TimeUnit.SECONDS.toNanos(1);
          branch.log(
              TreeLogger.TRACE,
              String.format("Compilation completed in %.02f seconds", compilationSeconds));
          if (workerException[0] != null) {
            throw workerException[0];
          }
        } catch (RuntimeException e) {
          throw e;
        } catch (Throwable e) {
          throw new RuntimeException("Exception processing units", e);
        } finally {
          buildQueue = null;
        }
        resultUnits.addAll(newlyBuiltUnits);
        builders.clear();

        // Resolve all newly built unit deps against the global classes.
        for (CompilationUnit unit : newlyBuiltUnits) {
          unit.getDependencies().resolve(allValidClasses);
        }

        /*
         * Invalidate any cached units with invalid refs.
         */
        Collection<CompilationUnit> invalidatedUnits = new ArrayList<CompilationUnit>();
        for (Iterator<Entry<CompilationUnitBuilder, CompilationUnit>> it =
                cachedUnits.entrySet().iterator();
            it.hasNext(); ) {
          Entry<CompilationUnitBuilder, CompilationUnit> entry = it.next();
          CompilationUnit unit = entry.getValue();
          boolean isValid = unit.getDependencies().validate(logger, allValidClasses);
          if (isValid && unit.isError()) {
            // See if the unit has classes that can't provide a
            // NameEnvironmentAnswer
            for (CompiledClass cc : unit.getCompiledClasses()) {
              try {
                cc.getNameEnvironmentAnswer();
              } catch (ClassFormatException ex) {
                isValid = false;
                break;
              }
            }
          }
          if (!isValid) {
            if (logger.isLoggable(TreeLogger.TRACE)) {
              logger.log(TreeLogger.TRACE, "Invalid Unit: " + unit.getTypeName());
            }
            invalidatedUnits.add(unit);
            builders.add(entry.getKey());
            it.remove();
          }
        }

        if (invalidatedUnits.size() > 0) {
          if (logger.isLoggable(TreeLogger.TRACE)) {
            logger.log(TreeLogger.TRACE, "Invalid units found: " + invalidatedUnits.size());
          }
        }

        // Any units we invalidated must now be removed from the valid classes.
        for (CompilationUnit unit : invalidatedUnits) {
          for (CompiledClass cc : unit.getCompiledClasses()) {
            allValidClasses.remove(cc.getSourceName());
          }
        }
      } while (builders.size() > 0);

      for (CompilationUnit unit : resultUnits) {
        unitCache.add(unit);
      }

      // Any remaining cached units are valid now.
      resultUnits.addAll(cachedUnits.values());

      // Done with a pass of the build - tell the cache its OK to cleanup
      // stale cache files.
      unitCache.cleanup(logger);

      // Sort, then report all errors (re-report for cached units).
      Collections.sort(resultUnits, CompilationUnit.COMPARATOR);
      logger = logger.branch(TreeLogger.DEBUG, "Validating units:");
      int errorCount = 0;
      for (CompilationUnit unit : resultUnits) {
        if (CompilationProblemReporter.reportErrors(logger, unit, suppressErrors)) {
          errorCount++;
        }
      }
      if (suppressErrors
          && errorCount > 0
          && !logger.isLoggable(TreeLogger.TRACE)
          && logger.isLoggable(TreeLogger.INFO)) {
        logger.log(
            TreeLogger.INFO,
            "Ignored "
                + errorCount
                + " unit"
                + (errorCount > 1 ? "s" : "")
                + " with compilation errors in first pass.\n"
                + "Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.");
      }
      return resultUnits;
    }