/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.internal.targets; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.references.IResolvedQualifiersReference; import org.apache.flex.compiler.definitions.references.ReferenceFactory; import org.apache.flex.compiler.exceptions.BuildCanceledException; import org.apache.flex.compiler.internal.graph.LinkReportWriter; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.projects.DependencyGraph; import org.apache.flex.compiler.internal.projects.LibraryPathManager; import org.apache.flex.compiler.internal.resourcebundles.ResourceBundleUtils; import org.apache.flex.compiler.internal.units.SWCCompilationUnit; import org.apache.flex.compiler.internal.workspaces.Workspace; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.ResourceBundleNotFoundForLocaleProblem; import org.apache.flex.compiler.problems.ResourceBundleNotFoundProblem; import org.apache.flex.compiler.problems.UnableToCreateLinkReportProblem; import org.apache.flex.compiler.targets.ITarget; import org.apache.flex.compiler.targets.ITargetProgressMonitor; import org.apache.flex.compiler.targets.ITargetReport; import org.apache.flex.compiler.targets.ITargetSettings; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.compiler.units.requests.IFileScopeRequestResult; import org.apache.flex.compiler.units.requests.IOutgoingDependenciesRequestResult; import org.apache.flex.swc.ISWC; import org.apache.flex.swc.ISWCLibrary; import org.apache.flex.swc.ISWCManager; import org.apache.flex.swc.ISWCScript; import org.apache.flex.utils.FileID; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; /** * Abstract base class to help subclasses implement ITarget. */ public abstract class Target implements ITarget { /** * Find all the externally visible definitions in the given compilation unit * list. * * @param compilationUnits A collection of compilation units. * @return All the externally visible definitions in the given compilation * unit list. * @throws InterruptedException Concurrency error. */ public static ImmutableList<IDefinition> getAllExternallyVisibleDefinitions( final Collection<ICompilationUnit> compilationUnits) throws InterruptedException { assert compilationUnits != null : "Expected collection of compilation units."; final ImmutableList.Builder<IDefinition> builder = new ImmutableList.Builder<IDefinition>(); for (final ICompilationUnit compilationUnit : compilationUnits) { final IFileScopeRequestResult result = compilationUnit.getFileScopeRequest().get(); builder.addAll(result.getExternallyVisibleDefinitions()); } return builder.build(); } /** * Constructor * * @param project {@link CompilerProject} that this target will be a part of. * @param targetSettings {@link ITargetSettings} for this target * @param progressMonitor {@link ITargetProgressMonitor} that will receive * progress information. Can be null, in which case no progress information * will be collected. */ protected Target(CompilerProject project, ITargetSettings targetSettings, ITargetProgressMonitor progressMonitor) { this.project = project; this.progressMonitor = progressMonitor; this.targetSettings = targetSettings; if (targetSettings.getASMetadataNames() != null) this.addASMetadataNames(targetSettings.getASMetadataNames()); } protected final CompilerProject project; protected final ITargetProgressMonitor progressMonitor; protected final ITargetSettings targetSettings; private LinkageChecker linkageChecker; private Set<String> metadataNames; private BuiltCompilationUnitSet builtCompilationUnits; /** * Lazily initialized {@link Iterable} of fatal {@link ICompilerProblem}s */ private Iterable<ICompilerProblem> fatalProblems; /** * Lazily initialized target report. */ private ITargetReport targetReport; /** * Discovers dependent compilation units from a set of root compilation * units. The return collection includes all compilation units in the * argument collection. * <p> * This method delegates the dependency discovery to * {@link Target#getDependentCompilationUnits}. Subclasses should override * this method if they have more sophisticated dependency discovery * algorithms. * * @param compilationUnits {@link ICompilationUnit}s known to be linked into * the target. Dependent compilation units will be added to this method. * @param problems Problems building compilation units. * @return All compilation units to link into the target. This collection * includes all elements in the {@code compilationUnits} collection from * argument. * @throws InterruptedException Concurrency error. */ protected Set<ICompilationUnit> findAllCompilationUnitsToLink(final Collection<ICompilationUnit> compilationUnits, final Collection<ICompilerProblem> problems) throws InterruptedException { assert compilationUnits != null : "compilation units can't be null"; assert problems != null : "problems can't be null"; final Set<ICompilationUnit> allCompilationUnitsInTarget = new HashSet<ICompilationUnit>(compilationUnits); final Set<ICompilationUnit> dependencies = getDependentCompilationUnits(allCompilationUnitsInTarget, problems); allCompilationUnitsInTarget.addAll(dependencies); return allCompilationUnitsInTarget; } /** * Waits for the specified {@link ICompilationUnit} to finish building and * add any problems found in the specified {@link ICompilationUnit} to the * specified {@link Collection}. * <p> * This method exists for the sole purpose of allowing the * {@link FlexLibrarySWFTarget} to filter out * {@link ResourceBundleNotFoundProblem}s and * {@link ResourceBundleNotFoundForLocaleProblem}s from * {@link SWCCompilationUnit}'s that are externally linked. * <p> * If we rip out support for Flex or if we are willing to report missing * resource bundles from external SWCs when linking a SWC, this method can * be inlined at its call site. * <p> * If we plan on continuing to support Flex, a better way to do this would * be to have the {@link IOutgoingDependenciesRequestResult} interface have method * to get all the resource bundles referenced by an {@link ICompilationUnit} * and wait to do the final resolution of resource bundles in {@link Target} * or one of its sub-classes. * * @param cu * @param problems * @throws InterruptedException */ protected void waitForCompilationUnitToFinish(final ICompilationUnit cu, final Collection<ICompilerProblem> problems) throws InterruptedException { cu.waitForBuildFinish(problems, getTargetType()); } /** * Triggers all the request method on all the compilation units and collect * problems from the project and the compilation units. * <p> * Although CSS can introduce dependent classes to be linked into the * target, the order of root classes and the dependencies doesn't matter. As * a result, there's no need to add dependency edges for * {@link DependencyGraph#topologicalSort()}. * * @param problems Problems will be returned here. * @param compilationUnits Compilation units to compile. * @throws InterruptedException */ private void buildCompilationUnits( final Collection<ICompilationUnit> compilationUnits, final Collection<ICompilerProblem> problems) throws InterruptedException { assert compilationUnits != null : "Expected compilation units."; assert problems != null : "Expected problem collection."; project.collectProblems(problems); // Parallelize the building of compilation units. for (final ICompilationUnit cu : compilationUnits) { if(isCanceled()) throw new BuildCanceledException(); cu.startBuildAsync(getTargetType()); } int numCompilationUnit = compilationUnits.size(); //As described in #updateProgress(int, int, int), each compilation unit //is considered to have two steps. The first step is considered completed //at the end of handle semantic analysis and the second is considered //completed when a compilation unit is fully compiled. We know that at //this moment, semantic analysis phase of all the known compilation units //would be completed successfully. Therefore, we assume that half of the //work we need to do in order to compile all the compilation units has //been done. Therefore, set the value to be the number of compilation unit //will be included in the build process. int totalCompUnitWorkCompleted = compilationUnits.size(); // Wait for all compilation units to finish building. for (final ICompilationUnit cu : compilationUnits) { waitForCompilationUnitToFinish(cu, problems); //Update the progress as we finished compiling a compilation unit. if(!updateProgress(numCompilationUnit, totalCompUnitWorkCompleted++, 95)) return; } } @Override public ITargetSettings getTargetSettings() { return targetSettings; } protected abstract ITargetReport computeTargetReport() throws InterruptedException; @Override public final ITargetReport getTargetReport() throws InterruptedException { if (targetReport == null) { project.getWorkspace().startBuilding(); try { targetReport = computeTargetReport(); } finally { project.getWorkspace().doneBuilding(); } } return targetReport; } /** * Discovers all the compilation units (roots and dependencies) that will be * linked into the target. Then builds these compilation units for all * phases of processing. * * @return A {@link BuiltCompilationUnitSet} that contains information about * all the {@link ICompilationUnit}s whose output contribute to this target * and {@link ICompilerProblem}s discovered while building all the * {@link ICompilationUnit}s. * @throws InterruptedException Concurrency error. */ protected BuiltCompilationUnitSet buildAllCompilationUnits() throws InterruptedException { Iterable<ICompilerProblem> fatalProblems = getFatalProblems(); if (!Iterables.isEmpty(fatalProblems)) return new BuiltCompilationUnitSet(ImmutableSet.<ICompilationUnit>of(), Collections.<ICompilerProblem>emptyList()); final HashSet<ICompilationUnit> compilationUnits = new HashSet<ICompilationUnit>(); final ArrayList<ICompilerProblem> problems = new ArrayList<ICompilerProblem>(); RootedCompilationUnits rootedCompilationUnits = getRootedCompilationUnits(); compilationUnits.addAll(rootedCompilationUnits.getUnits()); compilationUnits.addAll(findAllCompilationUnitsToLink(compilationUnits, problems)); buildCompilationUnits(compilationUnits, problems); return new BuiltCompilationUnitSet(ImmutableSet.<ICompilationUnit>copyOf(compilationUnits), problems); } protected BuiltCompilationUnitSet getBuiltCompilationUnitSet() throws InterruptedException { if (builtCompilationUnits == null) builtCompilationUnits = buildAllCompilationUnits(); return builtCompilationUnits; } /** * Computes the set of all all {@link ICompilationUnit}'s whose output is * part of the output of this target. This method does <b>NOT</b> compute * the dependencies introduced in CSS. * * @param compilationUnits A collection of root compilation units. * @param problems Problems building compilation units. * @return Set of all {@link ICompilationUnit}'s whose output is part of the * output of this target. The order of compilation units in this function * does not matter because we topologically sort the dependency graph later * when we are getting ready to add tags to the output SWF or SWC. * @throws InterruptedException */ protected final Set<ICompilationUnit> getDependentCompilationUnits( final Collection<ICompilationUnit> compilationUnits, final Collection<ICompilerProblem> problems) throws InterruptedException { final HashSet<ICompilationUnit> workSet = new HashSet<ICompilationUnit>(); final HashSet<ICompilationUnit> visitedSet = new HashSet<ICompilationUnit>(); int numCompUnitRemoved = 0; workSet.addAll(compilationUnits); while (workSet.size() > 0) { final ICompilationUnit currentUnit = workSet.iterator().next(); workSet.remove(currentUnit); if (visitedSet.add(currentUnit)) { //Increment num of the comp units removed from the workset, //so that we can calculate the total number of compilation units. //The reason we only increment it in this if block is that if //we don't hit in this if block, it means that we had removed //the same compilation unit before anyways, so we already //have taken it into account. numCompUnitRemoved++; currentUnit.startBuildAsync(getTargetType()); final DirectDependencies currentUnitDependencies = getDirectDependencies(currentUnit); final Iterable<ICompilationUnit> newCompilationUnitWork = currentUnitDependencies.dependencies; Iterables.addAll(problems, currentUnitDependencies.problems); for (ICompilationUnit cu : newCompilationUnitWork) { workSet.add(cu); cu.startBuildAsync(getTargetType()); } //Update the progress since we completed semantic analysis for one //compilation unit and possibly found more compilation units. if(!updateProgress((workSet.size()+numCompUnitRemoved), visitedSet.size(), 50)) return Collections.emptySet(); } } return visitedSet; } protected DirectDependencies getDirectDependencies(ICompilationUnit cu) throws InterruptedException { final Set<ICompilationUnit> dependencies = project.getDependencies(cu); return new DirectDependencies(dependencies, Collections.<ICompilerProblem>emptyList()); } /** * @return All the reachable compilation units in this job. */ public ImmutableList<ICompilationUnit> getReachableCompilationUnits(Collection<ICompilerProblem> problems) throws InterruptedException { RootedCompilationUnits rootedCompilationUnits = getRootedCompilationUnits(); final Set<ICompilationUnit> root = rootedCompilationUnits.getUnits(); Iterables.addAll(problems, rootedCompilationUnits.getProblems()); final List<ICompilationUnit> reachableCompilationUnitsInSWFOrder = project.getReachableCompilationUnitsInSWFOrder(root); final ImmutableList<ICompilationUnit> compilationUnits = ImmutableList.<ICompilationUnit> copyOf(reachableCompilationUnitsInSWFOrder); return compilationUnits; } /** * Create a link report at the path setup in the * targetSettings.getLinkReportPath. This method may be called after a * target has been built. * * @param problems * @throws InterruptedException */ protected void createLinkReport(Collection<ICompilerProblem> problems) throws InterruptedException { if (targetSettings.getLinkReport() != null) { OutputStream outStream; try { outStream = new FileOutputStream(targetSettings.getLinkReport()); // Ignore the problems found by getReachableCompilationUnits(). Those problems // should have already been found by the build. Collection<ICompilerProblem> reachableProblems = new ArrayList<ICompilerProblem>(); LinkReportWriter reportWriter = new LinkReportWriter(project.getDependencyGraph(), getReachableCompilationUnits(reachableProblems), getLinkageChecker()); reportWriter.writeToStream(outStream, problems); } catch (FileNotFoundException e) { final ICompilerProblem problem = new UnableToCreateLinkReportProblem( targetSettings.getLinkReport().getAbsolutePath()); problems.add(problem); } } } /** * Computes the set of compilation units that root the dependency walk. The * returned set of compilation units and their dependencies will be * compiled. * * @return The set of rooted {@link ICompilationUnit}'s. */ protected abstract RootedCompilationUnits computeRootedCompilationUnits() throws InterruptedException; public abstract RootedCompilationUnits getRootedCompilationUnits() throws InterruptedException; /** * Gets a set of {@link ICompilationUnit}s that are included into the build * process by -include-classes compiler argument. * <p> * This method is marked final until we have a use case to make it * non-final. * * @return a set of {@link ICompilationUnit}s that are included into the * build process by -include-classes compiler argument. */ public final Set<ICompilationUnit> getIncludesCompilationUnits() throws InterruptedException { Workspace workspace = project.getWorkspace(); Set<IResolvedQualifiersReference> includesReferences = new HashSet<IResolvedQualifiersReference>(); for (String className : targetSettings.getIncludes()) { IResolvedQualifiersReference ref = ReferenceFactory.packageQualifiedReference(workspace, className); includesReferences.add(ref); } return project.getScope().getCompilationUnitsForReferences(includesReferences); } /** * @return a collection of resource bundle compilation units that are included * into the build process by -include-resource-bundles compiler argument. */ public final Collection<ICompilationUnit> getIncludedResourceBundlesCompilationUnits(Collection<ICompilerProblem> problems) throws InterruptedException { Set<ICompilationUnit>includedResourceBundleCompUnits = new HashSet<ICompilationUnit>(); for (String bundleName : targetSettings.getIncludeResourceBundles()) { includedResourceBundleCompUnits.addAll(ResourceBundleUtils.findCompilationUnits(bundleName, project, problems)); } return includedResourceBundleCompUnits; } /** * Get the set of metadata names. * * @return The set of metadata names that will be preserved in a SWF * target or recorded in a SWC target. May be null if all metadata * names should be kept. */ @Override public final Set<String> getASMetadataNames() { return metadataNames; } /** * Add metadata names to the target. * * @param metadataNames metadata names that should be kept. May not be null. */ public void addASMetadataNames(Collection<String> metadataNames) { assert metadataNames != null; if (this.metadataNames == null) this.metadataNames = new HashSet<String>(); this.metadataNames.addAll(metadataNames); } /** * The value that represents the percentage of the main task that has been * completed so far. It should be between 0 and 100. */ private int percentCompleted = 0; /** * Gets called when build operation starts. */ protected final void buildStarted() { if(progressMonitor != null) { percentCompleted = 0; } project.getWorkspace().startBuilding(); } /** * Updates the value that represents the percentage of the work completed so * far using the information specified and notifies the progress monitor * about this value. Each compilation unit is assumed to complete 2 steps in * order to be considered compiled. The first step is considered to be done * when the semantic analysis phase is done. ( @see * CompilationUnitBase#handleOutgoingDependenciesRequest ). The second step * starts with byte code generation ( @see * CompilationUnitBase#handleABCBytesRequest ) and ends when swf tags * request is completed. ( @see CompilationUnitBase#handleSWFTagsRequest ) * * @param numTotalCompilationUnits number of compilation units known to be * included in the build process * @param totalCompilationUnitsWorkCompleted total compilation unit work * completed so far. As described above, each compilation unit has two steps. * Therefore, this value would be equal to (2*numTotalCompilationUnits), * when we are done with compiling all the compilation units. * @param maxPercentage the value that represents the maximum percentage of * the work can be considered completed so far in this method. This * method guarantees not to report a percentage value that is higher than * this value as completed to its clients via its progress monitor. * @return whether or not the build operation should continue. * <code>false</code> if the build operation has been requested to be * terminated, <code>true</code> otherwise. */ protected boolean updateProgress(int numTotalCompilationUnits, int totalCompilationUnitsWorkCompleted, int maxPercentage) { if(progressMonitor != null) { int percentage = (50 * totalCompilationUnitsWorkCompleted) / numTotalCompilationUnits; if(percentage > maxPercentage) percentage = maxPercentage; if(percentCompleted < percentage) { percentCompleted = percentage; progressMonitor.percentCompleted(this, percentCompleted); } return !isCanceled(); } return true; } /** * Updates the value that represents the percentage of the main task that * has been completed so far and notifies the progress monitor about this * value if any. * * @param percentageCompleted the percentage of the main task that has been * completed so far. * @return whether or not the build operation should continue. * <code>false</code> if the build operation has been requested to be * terminated, <code>true</code> otherwise. */ protected boolean updateProgress(int percentageCompleted) { if(progressMonitor != null) { if(this.percentCompleted < percentageCompleted) { this.percentCompleted = percentageCompleted; progressMonitor.percentCompleted(this, percentageCompleted); } return !isCanceled(); } return true; } /** * @return returns whether the current compilation operation has been * requested to be canceled. */ protected boolean isCanceled() { if(progressMonitor != null) return progressMonitor.isCanceled(this); return false; } /** * Gets called when build operation finishes. */ protected final void buildFinished() { if(progressMonitor != null) { percentCompleted = 100; progressMonitor.done(this); } project.getWorkspace().doneBuilding(); } /** * Determine if a compilation unit should be linked into the target. * * @param cu The compilation unit to test. * @param targetSettings The target settings. * @return true if the compilation unit's linkage is external, false * otherwise. * @throws InterruptedException */ protected boolean isLinkageExternal(ICompilationUnit cu, ITargetSettings targetSettings) throws InterruptedException { return getLinkageChecker().isExternal(cu); } /** * Get the target's linkage checker. * * @return the target's linkage checker. */ protected final LinkageChecker getLinkageChecker() { if (linkageChecker == null) linkageChecker = new LinkageChecker(project, targetSettings); return linkageChecker; } /** * Set the project's linkage checker. * * @param linkageChecker linkage checker, may not be null. */ protected void setLinkageChecker(LinkageChecker linkageChecker) { // validate we are using just one linkage checker for // the life of a target. assert this.linkageChecker == null; this.linkageChecker = linkageChecker; assert this.linkageChecker != null; } protected Iterable<ICompilerProblem> getFatalProblems() throws InterruptedException { if (fatalProblems == null) fatalProblems = computeFatalProblems(); return fatalProblems; } /** * Computes an {@link Iterable} of fatal {@link ICompilerProblem}s that * prevent this {@link Target} from being built. * <p> * Sub-classes override this method to check for additional fatal * {@link ICompilerProblem}s. * * @return {@link Iterable} of fatal {@link ICompilerProblem}s * @throws InterruptedException */ protected Iterable<ICompilerProblem> computeFatalProblems() throws InterruptedException { return ImmutableList.copyOf(project.getFatalProblems()); } /** * Get a collection of compilation units for all of the classes found in * all of the libraries found in the -include-libraries path. * * @return collection of compilation units for all the classes found on * the -include-libraries path. */ protected final Collection<ICompilationUnit> getIncludeLibrariesCompilationUnits() { //Collection<ICompilationUnit> units = new HashSet<ICompilationUnit>(); Set<IResolvedQualifiersReference> includeLibrariesReferences = new HashSet<IResolvedQualifiersReference>(); // Find all of the libraries on the -include-library path Set<FileID> swcs = LibraryPathManager.discoverSWCFilePaths( targetSettings.getIncludeLibraries().toArray(new File[0])); // For each library, get a compilation unit for every class in the // library. ISWCManager swcManager = project.getWorkspace().getSWCManager(); Workspace w = project.getWorkspace(); for (FileID swcPath : swcs) { File swcFile =swcPath.getFile(); // If the SWC does not exist on disk, just ignore it for now, and a // problem will be created later on during the LibraryPathManager.collectionProblems() call. if (!swcFile.exists()) continue; ISWC swc = swcManager.get(swcFile); for (ISWCLibrary library : swc.getLibraries()) { for (ISWCScript script : library.getScripts()) { for (String def : script.getDefinitions()) { IResolvedQualifiersReference definitionRef = ReferenceFactory.packageQualifiedReference(w, def); includeLibrariesReferences.add(definitionRef); } } } } return project.getScope().getCompilationUnitsForReferences(includeLibrariesReferences); } /** * Wad that holds a set of {@link ICompilationUnit}s that have been built to * build the output of this target and any {@link ICompilerProblem}s found * building those {@link ICompilationUnit}s. */ protected static final class BuiltCompilationUnitSet { BuiltCompilationUnitSet(ImmutableSet<ICompilationUnit> compilationUnits, Iterable<ICompilerProblem> problems) { this.compilationUnits = compilationUnits; this.problems = problems; } public final ImmutableSet<ICompilationUnit> compilationUnits; final Iterable<ICompilerProblem> problems; } /** * Wad containing the set of {@link ICompilationUnit}s that root the dependency walk * and any {@link ICompilerProblem}s encountered computing the roots of the * dependency walk. The returned set of compilation units and their * dependencies will be compiled. * */ public static final class RootedCompilationUnits { static RootedCompilationUnits concat(RootedCompilationUnits base, Set<ICompilationUnit> units, Iterable<ICompilerProblem> problems) { return new RootedCompilationUnits(Sets.union(base.units, units), Iterables.concat(base.problems, problems)); } public RootedCompilationUnits(Set<ICompilationUnit> units, Iterable<ICompilerProblem> problems) { assert units != null; assert problems != null; this.units = units; this.problems = problems; } private final Set<ICompilationUnit> units; private final Iterable<ICompilerProblem> problems; public Set<ICompilationUnit> getUnits() { return units; } public Iterable<ICompilerProblem> getProblems() { return problems; } } /** * Wad containing an {@link Iterable} of {@link ICompilationUnit}s and an * {@link Iterable} of {@link ICompilerProblem}s found while constructing * the {@link Iterable} of {@link ICompilationUnit}s. * <p> * The {@link Iterable} of {@link ICompilationUnit}s iterates over all the * {@link ICompilationUnit}s that are directly depended on by another * {@link ICompilationUnit}. */ public static class DirectDependencies { DirectDependencies(Iterable<ICompilationUnit> dependencies, Iterable<ICompilerProblem> problems) { this.dependencies = dependencies; this.problems = problems; } /** * Creates a new {@link DirectDependencies} object that is the concatenation of two other * {@link DirectDependencies} objects. * @param a * @param b * @return a new {@link DirectDependencies} object that is the concatenation of two other * {@link DirectDependencies} objects. */ static DirectDependencies concat(DirectDependencies a, DirectDependencies b) { return new DirectDependencies(Iterables.concat(a.dependencies, b.dependencies), Iterables.concat(a.problems, b.problems)); } final Iterable<ICompilationUnit> dependencies; final Iterable<ICompilerProblem> problems; } }