org.apache.flex.compiler.internal.targets.SWFTarget.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.flex.compiler.internal.targets.SWFTarget.java

Source

/*
 *
 *  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.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.flex.abc.ABCLinker;
import org.apache.flex.compiler.config.RSLSettings;
import org.apache.flex.compiler.constants.IASLanguageConstants;
import org.apache.flex.compiler.constants.IMetaAttributeConstants;
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.definitions.ClassDefinition;
import org.apache.flex.compiler.internal.projects.CompilerProject;
import org.apache.flex.compiler.internal.projects.FlexProject;
import org.apache.flex.compiler.internal.scopes.ASProjectScope;
import org.apache.flex.compiler.internal.units.SWCCompilationUnit;
import org.apache.flex.compiler.problems.ICompilerProblem;
import org.apache.flex.compiler.targets.ISWFTarget;
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.ICompilationUnit.UnitType;
import org.apache.flex.swc.ISWC;
import org.apache.flex.swc.ISWCLibrary;
import org.apache.flex.swf.ISWF;
import org.apache.flex.swf.SWF;
import org.apache.flex.swf.SWFFrame;
import org.apache.flex.swf.ISWFConstants;
import org.apache.flex.swf.tags.DoABCTag;
import org.apache.flex.swf.tags.IManagedTag;
import org.apache.flex.swf.tags.ITag;
import org.apache.flex.swf.tags.ScriptLimitsTag;
import org.apache.flex.swf.tags.SymbolClassTag;
import org.apache.flex.swf.types.RGB;
import org.apache.flex.swf.types.Rect;
import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

/**
 * Concrete implementation of ITarget for building a collection of source files
 * into a SWF.
 */
public abstract class SWFTarget extends Target implements ISWFTarget {
    protected static final class SWFFrameInfo {
        public static final boolean EXTERNS_ALLOWED = true;
        public static final boolean EXTERNS_DISALLOWED = false;

        public SWFFrameInfo(String frameLabel, boolean allowExternals, Set<ICompilationUnit> rootedUnits,
                Iterable<ICompilerProblem> problems) {
            // character replaced came from the old compiler, so copy to match behavior
            this.frameLabel = frameLabel != null ? frameLabel.replaceAll("[^A-Za-z0-9]", "_") : null;
            this.allowExternals = allowExternals;
            this.rootedUnits = rootedUnits;
            this.problems = problems;
        }

        public SWFFrameInfo(Set<ICompilationUnit> rootedUnits, Iterable<ICompilerProblem> problems) {
            this(null, EXTERNS_ALLOWED, rootedUnits, problems);
        }

        public final String frameLabel;
        public final boolean allowExternals;
        public final Set<ICompilationUnit> rootedUnits;
        public final Iterable<ICompilerProblem> problems;
    }

    public SWFTarget(CompilerProject project, ITargetSettings targetSettings,
            ITargetProgressMonitor progressMonitor) {
        super(project, targetSettings, progressMonitor);
    }

    private Collection<ICompilerProblem> problemCollection;
    private Target.RootedCompilationUnits rootedCompilationUnits;
    protected Set<String> metadataDonators = new HashSet<String>();

    /** 
     * Cached list of compilation units. This is a performance optimization to keep us from
     * making redundant calls to topologicalSort.
     * 
     * Note that this optimization only gives a small boost in observed cases.
     * 
     * if non-null, this is the results of calling project.getReachableCompilationUnitsInSWFOrder(rootedCU);
     * We will assume it doesn't change
     */

    private List<ICompilationUnit> rootedCompilationUnitsAndDependenciesInSWFOrder;

    /**
     * Cached information about all the frames in the SWF.
     */
    private FramesInformation framesInformation;

    /**
     * Cached {@link RGB} value for the SWF's background color.
     */
    private RGB swfBackgroundColor;

    /**
     * Cached {@link ITargetAttributes} for the SWF
     */
    private ITargetAttributes _targetAttributes;

    /**
     * Gets the set of {@link ICompilationUnit}s that are the roots of the graph of
     * {@link ICompilationUnit}s whose output will be in the generate SWF.
     * @return The set of {@link ICompilationUnit}s that are the roots of the graph of
     * {@link ICompilationUnit}s whose output will be in the generate SWF
     */
    @Override
    public RootedCompilationUnits getRootedCompilationUnits() throws InterruptedException {
        if (rootedCompilationUnits == null)
            rootedCompilationUnits = computeRootedCompilationUnits();
        return rootedCompilationUnits;
    }

    /**
     * Same as project.getReachableCompilationUnitsInSWFOrder(), but with a cache for the case of rooted compilation units
     * @throws InterruptedException 
     */
    List<ICompilationUnit> getReachableCompilationUnitsInSWFOrder(Collection<ICompilationUnit> roots)
            throws InterruptedException {
        List<ICompilationUnit> ret = null;

        final RootedCompilationUnits rootedCompilationUnits = getRootedCompilationUnits();

        // Determine if the "roots" in this call are the rooted compilation units for this target.
        // We only cached the results for this case
        boolean passedInRootedCompilationUnits = false;
        if (roots.size() == rootedCompilationUnits.getUnits().size()) {
            passedInRootedCompilationUnits = true;
            for (ICompilationUnit cu : roots) {
                if (!rootedCompilationUnits.getUnits().contains(cu))
                    passedInRootedCompilationUnits = false;
            }
        }

        // If this is the case that is cached, then get/make the cache
        if (passedInRootedCompilationUnits) {
            if (rootedCompilationUnitsAndDependenciesInSWFOrder == null) {
                rootedCompilationUnitsAndDependenciesInSWFOrder = project
                        .getReachableCompilationUnitsInSWFOrder(roots);
            }
            ret = rootedCompilationUnitsAndDependenciesInSWFOrder;
        } else {
            // If not the cached case, just call the function directly
            ret = project.getReachableCompilationUnitsInSWFOrder(roots);
        }
        assert ret != null;

        return ret;
    }

    /**
     * Absolute path of the path that contains the reference to Object.
     * We use this to determine which SWC contains native code so that
     * we always exclude all of the definitions from that SWC.
     */
    private String pathContainingObject;

    @Override
    public ISWF build(Collection<ICompilerProblem> problems) {
        buildStarted();
        try {
            Iterable<ICompilerProblem> fatalProblems = getFatalProblems();
            if (!Iterables.isEmpty(fatalProblems)) {
                Iterables.addAll(problems, fatalProblems);
                return null;
            }

            Set<ICompilationUnit> compilationUnitSet = new HashSet<ICompilationUnit>();
            Target.RootedCompilationUnits rootedCompilationUnits = getRootedCompilationUnits();

            // no rooted compilation could be found, but still create an empty SWF
            // in this error case
            if (rootedCompilationUnits.getUnits().isEmpty())
                return buildEmptySWF();

            compilationUnitSet.addAll(rootedCompilationUnits.getUnits());

            this.problemCollection = problems;

            FramesInformation frames = getFramesInformation();

            BuiltCompilationUnitSet builtCompilationUnits = getBuiltCompilationUnitSet();
            Iterables.addAll(problems, builtCompilationUnits.problems);

            doPostBuildWork(builtCompilationUnits.compilationUnits, problems);

            ISWF swf = initializeSWF(getReachableCompilationUnitsInSWFOrder(rootedCompilationUnits.getUnits()));

            // now that everything is built, the dependency graph is populated enough to do a topological sort on
            // all compilation units needed by this target.
            // The compilation units that define bases classes must occurs in the swf before
            // compilation units that define classes that subclass those classes ( see
            // inheritance dependencies in the {@link DependencyGraph} ).
            Set<ICompilationUnit> emittedCompilationUnits = new HashSet<ICompilationUnit>();

            frames.createFrames(this, swf, builtCompilationUnits.compilationUnits, emittedCompilationUnits,
                    problems);

            createLinkReport(problems);

            // "Link" the resulting swf, if the optimize flag is set
            return linkSWF(swf);
        } catch (BuildCanceledException bce) {
            return null;
        } catch (InterruptedException ie) {
            return null;
        } finally {
            buildFinished();
        }
    }

    @Override
    public TargetType getTargetType() {
        return TargetType.SWF;
    }

    protected final ITargetAttributes getTargetAttributes() throws InterruptedException {
        // if already computed, just return the cached value
        if (_targetAttributes == null) {
            // first time - delegate to subclass to compute
            _targetAttributes = computeTargetAttributes();
        }
        return _targetAttributes;
    }

    /**
     * round-up  user specified target attributes
     * All derived classes must provide one, but they are free to provide "do nothing" implementations
     * (like NilTargetAttributes)
     */
    protected abstract ITargetAttributes computeTargetAttributes() throws InterruptedException;

    /**
     * Create the {@link FramesInformation} which contains the skeleton for the frames
     * of this SWF. The actual frames will be create in doCreateFrames().
     * @throws InterruptedException 
     */
    protected abstract FramesInformation computeFramesInformation() throws InterruptedException;

    protected final FramesInformation getFramesInformation() throws InterruptedException {
        if (framesInformation != null)
            return framesInformation;
        framesInformation = computeFramesInformation();
        return framesInformation;
    }

    /**
     * Check the build and analyze the results before the SWF target is
     * initialized.
     * 
     * @param compilationUnits The set of compilation units after
     * buildAndCollectProblems() has run.
     * @param problems A collection where discovered problems are appended.
     */
    protected void doPostBuildWork(ImmutableSet<ICompilationUnit> compilationUnits,
            Collection<ICompilerProblem> problems) throws InterruptedException {

    }

    /**
     * Creates a new instance of a SWF.
     * 
     * @return a new instance of a SWF.
     */
    protected SWF buildEmptySWF() {
        return new SWF();
    }

    /**
     * Add to the collection of compiler problems.
     * 
     * @param problem
     */
    protected void reportProblem(ICompilerProblem problem) {
        assert problemCollection != null;

        problemCollection.add(problem);
    }

    /**
     * Add a set of root classes and its dependencies to a new frame or to an existing
     * frame.
     * @param frame if null a new frame will be created for the classes. Otherwise the 
     * classes will be added to the existing frame.
     * @param frameRootClasses
     * @param projectScope
     * @param allowExternals
     * @param emittedCompilationUnits
     * @return The SWF frame.
     * @throws InterruptedException
     */
    protected SWFFrame createWithClassesAndItsDependencies(SWFFrame frame,
            Collection<ClassDefinition> frameRootClasses, ASProjectScope projectScope, boolean allowExternals,
            Set<ICompilationUnit> emittedCompilationUnits) throws InterruptedException {
        final List<ICompilationUnit> rootedUnitsForFrame = new LinkedList<ICompilationUnit>();
        for (ClassDefinition frameRootClass : frameRootClasses) {
            final Collection<IDefinition> extraDefinitions = frameRootClass.resolveExtraClasses(project);
            final ICompilationUnit frameFactoryClassCompilationUnit = projectScope
                    .getCompilationUnitForDefinition(frameRootClass);
            assert frameFactoryClassCompilationUnit != null;
            rootedUnitsForFrame.add(frameFactoryClassCompilationUnit);
            for (IDefinition extraDef : extraDefinitions) {
                if (!extraDef.isImplicit()) {
                    ICompilationUnit extraDefCompilationUnit = projectScope
                            .getCompilationUnitForDefinition(extraDef);
                    assert extraDefCompilationUnit != null;
                    rootedUnitsForFrame.add(extraDefCompilationUnit);
                }
            }
        }

        if (frame == null)
            frame = new SWFFrame();

        if (!addCompilationUnitsAndDependenciesToFrame(frame, rootedUnitsForFrame, allowExternals,
                emittedCompilationUnits))
            return null;

        return frame;
    }

    protected boolean addCompilationUnitsAndDependenciesToFrame(SWFFrame frame,
            Collection<ICompilationUnit> rootedUnitsForFrame, boolean allowExternals,
            Set<ICompilationUnit> emittedCompilationUnits) throws InterruptedException {
        List<ICompilationUnit> unitsForFrame = this.getReachableCompilationUnitsInSWFOrder(rootedUnitsForFrame);
        for (ICompilationUnit cu : unitsForFrame) {
            if (emittedCompilationUnits.add(cu)) {
                boolean includeCu = testCompilationUnitLinkage(cu, allowExternals);
                doAddMetadataNamesToTarget(cu, includeCu);

                if (includeCu) {
                    boolean tagsAdded = cu.getSWFTagsRequest().get().addToFrame(frame);
                    if (!tagsAdded)
                        return false;
                }
            }
        }
        return true;
    }

    /**
     * Add metadata names to the target for this compilation unit.
     * 
     * @param cu
     * @param linkedIn true if the compilation unit is linked in, false if
     * external.
     */
    protected void doAddMetadataNamesToTarget(ICompilationUnit cu, boolean linkedIn) {

        if (shouldAddMetadataNamesToTarget(cu, linkedIn)) {
            if (metadataDonators.add(cu.getAbsoluteFilename())) {
                ISWC swc = project.getWorkspace().getSWCManager().get(new File(cu.getAbsoluteFilename()));
                for (ISWCLibrary library : swc.getLibraries()) {
                    addASMetadataNames(library.getKeepAS3MetadataSet());
                }
            }

        }
    }

    /**
     * Test if any metadata names associated with the compilation unit should be
     * added to the target.
     * 
     * @param cu
     * @param linkedIn true if the compilation unit is linked in, false if
     * external.
     * @return true if the metadata names should be included, false otherwise.
     */
    protected boolean shouldAddMetadataNamesToTarget(ICompilationUnit cu, boolean linkedIn) {
        return (cu.getCompilationUnitType() == UnitType.SWC_UNIT && !isLinkageAlwaysExternal(cu));
    }

    /**
     * Test if a compilation unit should be include in this target.
     * 
     * @param cu
     * @param allowExternals
     * @return True if the compilation should be included, false otherwise.
     * @throws InterruptedException
     */
    protected boolean testCompilationUnitLinkage(ICompilationUnit cu, boolean allowExternals)
            throws InterruptedException {
        boolean includeCu = true;

        if (!allowExternals)
            includeCu = !isLinkageAlwaysExternal(cu);
        else if (isLinkageExternal(cu, targetSettings))
            includeCu = false;

        return includeCu;
    }

    /**
     * Test if this compilation unit should always be externalized. Native code
     * cannot be included in an application. We for test native code by checking
     * for the SWC that contains the definition of Object and externalize all of
     * the classes in that SWC. Compilation units that come from an ANE are also
     * always externalized.
     * 
     * @param cu
     * @return true if the compilation unit should always be externalized, false
     * otherwise.
     */
    private boolean isLinkageAlwaysExternal(ICompilationUnit cu) {
        if (cu.getCompilationUnitType() != UnitType.SWC_UNIT)
            return false;

        // Find the SWC that contains Object.
        if (pathContainingObject == null) {
            IResolvedQualifiersReference objectReference = ReferenceFactory
                    .packageQualifiedReference(project.getWorkspace(), IASLanguageConstants.Object);
            Set<ICompilationUnit> units = project.getScope()
                    .getCompilationUnitsForReferences(Collections.singletonList(objectReference));
            assert units.size() == 1;

            pathContainingObject = units.iterator().next().getAbsoluteFilename();
            assert pathContainingObject != null;
        }

        // If this compilation unit comes from the same SWC as the SWC that
        // contains Object then we must always extern the class.
        if (pathContainingObject.equals(cu.getAbsoluteFilename()))
            return true;

        // Test if the compilation unit is from an ANE file.
        if (cu instanceof SWCCompilationUnit)
            return ((SWCCompilationUnit) cu).isANE();

        return false;
    }

    protected abstract void addLinkedABCToFrame(SWFFrame targetFrame, Iterable<DoABCTag> inputABCs,
            ABCLinker.ABCLinkerSettings linkSettings) throws Exception;

    protected abstract void setKeepAS3MetadataLinkerSetting(ABCLinker.ABCLinkerSettings linkSettings);

    /**
     * Link the swf - this handles merging the DoABC tags inside each frame, and will
     * also handle stripping debug opcodes, optimizing the abcs, and/or stripping metadata
     * @param unLinked  the SWF to process
     * @return          A SWF that is the resulting of merging, optimizing, etc.
     */
    protected ISWF linkSWF(ISWF unLinked) {
        SWF result = new SWF();
        if (unLinked.getBackgroundColor() != null)
            result.setBackgroundColor(unLinked.getBackgroundColor());
        result.setEnableDebugger2(unLinked.getEnableDebugger2());
        result.setFrameRate(unLinked.getFrameRate());
        result.setFrameSize(unLinked.getFrameSize());
        result.setMetadata(unLinked.getMetadata());
        ScriptLimitsTag scriptLimits = unLinked.getScriptLimits();
        if (scriptLimits != null)
            result.setScriptLimits(scriptLimits.getMaxRecursionDepth(), scriptLimits.getScriptTimeoutSeconds());
        result.setTopLevelClass(unLinked.getTopLevelClass());
        result.setUseAS3(unLinked.getUseAS3());
        result.setUseDirectBlit(unLinked.getUseDirectBlit());
        result.setUseGPU(unLinked.getUseGPU());
        result.setUseNetwork(unLinked.getUseNetwork());
        result.setVersion(unLinked.getVersion());
        result.setProductInfo(unLinked.getProductInfo());

        ITargetSettings settings = getTargetSettings();

        ABCLinker.ABCLinkerSettings linkSettings = new ABCLinker.ABCLinkerSettings();

        linkSettings.setOptimize(settings.isOptimized());
        linkSettings.setEnableInlining(project.isInliningEnabled());
        linkSettings.setStripDebugOpcodes(!settings.isDebugEnabled());
        linkSettings.setStripGotoDefinitionHelp(!settings.isDebugEnabled());
        linkSettings.setStripFileAttributeFromGotoDefinitionHelp(settings.isOptimized());
        linkSettings.setProblemsCollection(this.problemCollection);
        linkSettings.setRemoveDeadCode(settings.getRemoveDeadCode());

        Collection<String> metadataNames = getASMetadataNames();
        if (settings.isDebugEnabled() && metadataNames != null) {
            Collection<String> names = new ArrayList<String>(metadataNames);
            names.add(IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITIONHELP);
            names.add(IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITION_CTOR_HELP);
            metadataNames = names;
        }

        setKeepAS3MetadataLinkerSetting(linkSettings);

        for (int i = 0; i < unLinked.getFrameCount(); ++i) {
            SWFFrame unlinkedFrame = unLinked.getFrameAt(i);
            SWFFrame resultFrame = new SWFFrame();

            if (unlinkedFrame.getName() != null)
                resultFrame.setName(unlinkedFrame.getName(), unlinkedFrame.hasNamedAnchor());

            LinkedList<DoABCTag> accumulatedABC = new LinkedList<DoABCTag>();
            for (ITag unlinkedTag : unlinkedFrame) {
                if (unlinkedTag instanceof DoABCTag) {
                    final DoABCTag abcTag = (DoABCTag) unlinkedTag;
                    accumulatedABC.add(abcTag);
                } else {
                    if (!accumulatedABC.isEmpty()) {
                        try {
                            addLinkedABCToFrame(resultFrame, accumulatedABC, linkSettings);
                        } catch (Exception e) {
                            return unLinked;
                        }
                        accumulatedABC.clear();
                    }

                    if (!(unlinkedTag instanceof IManagedTag)) {
                        resultFrame.addTag(unlinkedTag);
                    } else if (unlinkedTag instanceof SymbolClassTag) {
                        SymbolClassTag s = (SymbolClassTag) unlinkedTag;
                        for (String symbol_name : s.getSymbolNames()) {
                            resultFrame.defineSymbol(s.getSymbol(symbol_name), symbol_name);
                        }
                    }
                }
            }
            if (!accumulatedABC.isEmpty()) {
                try {
                    addLinkedABCToFrame(resultFrame, accumulatedABC, linkSettings);
                } catch (Exception e) {
                    return unLinked;
                }
                accumulatedABC.clear();
            }
            result.addFrame(resultFrame);
        }

        return result;
    }

    /**
     * {@inheritDoc}
     * <p>
     * For {@link SWFTarget}'s the set of rooted {@link ICompilationUnit}s is computed
     * by enumerating all the frames and collecting all the {@link ICompilationUnit}s assigned
     * to each of the frames.
     */
    @Override
    protected RootedCompilationUnits computeRootedCompilationUnits() throws InterruptedException {
        final FramesInformation framesInfo = getFramesInformation();
        final RootedCompilationUnits rootedCompilationUnits = new RootedCompilationUnits(
                ImmutableSet.copyOf(framesInfo.getAllCompilationUnits()), framesInfo.getProblems());
        return rootedCompilationUnits;
    }

    /**
     * Initialize SWF model with default header values from the target settings
     * when not overridden by the target attributes.
     * 
     * @return SWF model.
     * @throws InterruptedException 
     */
    protected ISWF initializeSWF(List<ICompilationUnit> reachableCompilationUnits) throws InterruptedException {
        int swfVersion = targetSettings.getSWFVersion();

        int swfWidth = targetSettings.getDefaultWidth();

        ITargetAttributes targetAttributes = getTargetAttributes();
        Float attrWidth = targetAttributes.getWidth();
        if (attrWidth != null)
            swfWidth = attrWidth.intValue();

        int swfHeight = targetSettings.getDefaultHeight();
        Float attrHeight = targetAttributes.getHeight();
        if (attrHeight != null)
            swfHeight = attrHeight.intValue();

        Rect swfFrameSize = new Rect(ISWFConstants.TWIPS_PER_PIXEL * swfWidth,
                ISWFConstants.TWIPS_PER_PIXEL * swfHeight);

        float swfFrameRate = targetSettings.getDefaultFrameRate();
        Float attrFrameRate = targetAttributes.getFrameRate();
        if (attrFrameRate != null)
            swfFrameRate = attrFrameRate.floatValue();

        boolean swfUseDirectBlit = targetSettings.useDirectBlit();
        Boolean attrUseDirectBlit = targetAttributes.getUseDirectBlit();
        if (attrUseDirectBlit != null)
            swfUseDirectBlit = attrUseDirectBlit.booleanValue();

        boolean swfUseGPU = targetSettings.useGPU();
        Boolean attrUseGPU = targetAttributes.getUseGPU();
        if (attrUseGPU != null)
            swfUseGPU = attrUseGPU.booleanValue();

        final RGB swfBackgroundColorRGB = getBackgroundColor();

        SWF swf = new SWF();
        swf.setVersion(swfVersion);
        swf.setFrameSize(swfFrameSize);
        swf.setFrameRate(swfFrameRate);
        swf.setUseDirectBlit(swfUseDirectBlit);
        swf.setUseGPU(swfUseGPU);
        swf.setBackgroundColor(swfBackgroundColorRGB);
        swf.setUseAS3(swfVersion >= 9);
        swf.setUseNetwork(targetSettings.useNetwork());
        swf.setMetadata(targetSettings.getSWFMetadata());

        // Apply the ScriptLimits tag, but only if limits have been specified
        // either in the targetSettings or targetAttributes
        Integer attrScriptRecursionLimit = targetAttributes.getScriptRecursionLimit();
        Integer attrScriptTimeLimit = targetAttributes.getScriptTimeLimit();
        if (targetSettings.areDefaultScriptLimitsSet() || attrScriptRecursionLimit != null
                || attrScriptTimeLimit != null) {
            int swfMaxRecursionDepth = targetSettings.getDefaultScriptRecursionLimit();
            if (attrScriptRecursionLimit != null)
                swfMaxRecursionDepth = attrScriptRecursionLimit.intValue();

            int swfScriptTimeoutSeconds = targetSettings.getDefaultScriptTimeLimit();
            if (attrScriptTimeLimit != null)
                swfScriptTimeoutSeconds = attrScriptTimeLimit.intValue();

            swf.setScriptLimits(swfMaxRecursionDepth, swfScriptTimeoutSeconds);
        }

        return swf;
    }

    protected final RGB getBackgroundColor() throws InterruptedException {
        if (swfBackgroundColor != null)
            return swfBackgroundColor;

        int swfBackgroundColorInt = targetSettings.getDefaultBackgroundColor();
        String attrBackgroundColorString = getTargetAttributes().getBackgroundColor();
        if (!Strings.isNullOrEmpty(attrBackgroundColorString)) {
            if (project instanceof FlexProject)
                swfBackgroundColorInt = ((FlexProject) project).getColorAsInt(attrBackgroundColorString);
            else
                swfBackgroundColorInt = Integer.decode(attrBackgroundColorString).intValue();
        }
        swfBackgroundColor = new RGB(swfBackgroundColorInt);
        return swfBackgroundColor;
    }

    @Override
    protected ITargetReport computeTargetReport() throws InterruptedException {
        BuiltCompilationUnitSet builtCompilationUnits = getBuiltCompilationUnitSet();

        return new TargetReport(project, builtCompilationUnits.compilationUnits,
                Collections.<RSLSettings>emptyList(), getBackgroundColor(), targetSettings, getTargetAttributes(),
                getLinkageChecker());
    }

    /**
     * Contains information about the skeleton of a SWF that is being built by
     * a {@link SWFTarget} and provides methods to create {@link SWFFrame}s and add them
     * to a {@link ISWF}.
     */
    protected static class FramesInformation {
        public FramesInformation(Iterable<SWFFrameInfo> frameInfos) {
            this.frameInfos = frameInfos;
        }

        /**
         * {@link Iterable} of {@link SWFFrameInfo}s which represents the
         * skeleton of a SWF being built by a {@link SWFTarget}.
         */
        public final Iterable<SWFFrameInfo> frameInfos;

        /**
         * @return An {@link Iterable} of {@link ICompilerProblem}s that can be
         * used to iterate all the {@link ICompilerProblem}s found while
         * building the skeleton of a SWF being built by a {@link SWFTarget}.
         */
        final Iterable<ICompilerProblem> getProblems() {
            Iterable<Iterable<ICompilerProblem>> problemIterables = Iterables.transform(frameInfos,
                    new Function<SWFFrameInfo, Iterable<ICompilerProblem>>() {

                        @Override
                        public Iterable<ICompilerProblem> apply(SWFFrameInfo frame) {
                            return frame.problems;
                        }
                    });

            return Iterables.concat(problemIterables);
        }

        /**
         * @return An {@link Iterable} of {@link ICompilationUnit}s that are rooted by
         * the skeleton of a SWF buing built by a {@link SWFTarget}.
         */
        final Iterable<ICompilationUnit> getAllCompilationUnits() {
            Iterable<Iterable<ICompilationUnit>> compilationUnitIterables = Iterables.transform(frameInfos,
                    new Function<SWFFrameInfo, Iterable<ICompilationUnit>>() {

                        @Override
                        public Iterable<ICompilationUnit> apply(SWFFrameInfo frame) {
                            return frame.rootedUnits;
                        }
                    });
            return Iterables.concat(compilationUnitIterables);
        }

        /**
         * Creates a {@link SWFFrame} for a {@link SWFFrameInfo}.
         * 
         * @param swfTarget The {@link SWFTarget} that is building the SWF to
         * which the newly created {@link SWFFrame} will be added.
         * @param frameInfo The {@link SWFFrameInfo} that represents the skeleton
         * of the SWF frame to create.
         * @param builtCompilationUnits The {@link ImmutableSet} of
         * {@link ICompilationUnit}s that have been built to create the SWF
         * being built by the specified {@link SWFTarget}. This {@link Set} is
         * used to write an assert.
         * @param emittedCompilationUnits The {@link Set} of
         * {@link ICompilationUnit}s that any {@link ICompilationUnit}s added to
         * this frame should be added to. This {@link Set} is used to ensure
         * that each {@link ICompilationUnit} is only added to a single frame in
         * a SWF.
         * @param problems {@link Collection} of {@link ICompilerProblem}s that
         * any {@link ICompilerProblem}s from any {@link ICompilationUnit} added
         * to the new {@link SWFFrame} should be added to.
         * @return A new {@link SWFFrame}.
         * @throws InterruptedException
         */
        protected final SWFFrame createFrame(SWFTarget swfTarget, SWFFrameInfo frameInfo,
                ImmutableSet<ICompilationUnit> builtCompilationUnits, Set<ICompilationUnit> emittedCompilationUnits,
                Collection<ICompilerProblem> problems) throws InterruptedException {
            Iterables.addAll(problems, frameInfo.problems);

            final SWFFrame swfFrame = new SWFFrame();

            if (frameInfo.frameLabel != null)
                swfFrame.setName(frameInfo.frameLabel, true);

            assert Sets.difference(frameInfo.rootedUnits, builtCompilationUnits)
                    .isEmpty() : "All compilation units to emit on this frame should have been built!";

            if (!swfTarget.addCompilationUnitsAndDependenciesToFrame(swfFrame, frameInfo.rootedUnits,
                    frameInfo.allowExternals, emittedCompilationUnits)) {
                return null;
            }
            return swfFrame;
        }

        /**
         * Creates all the {@link SWFFrame}s for the SWF skeleton represented by
         * this {@link FramesInformation} and adds them to the specified
         * {@link ISWF}.
         * <p>
         * This method is overridden by sub-classes of {@link FramesInformation}.
         * 
         * @param swfTarget The {@link SWFTarget} that is building the SWF to
         * which the newly created {@link SWFFrame}s will be added.
         * @param swf The {@link ISWF} to which the new created {@link SWFFrame}
         * s will be added.
         * @param builtCompilationUnits The {@link ImmutableSet} of
         * {@link ICompilationUnit}s that have been built to create the SWF
         * being built by the specified {@link SWFTarget}. Sub-classes use this
         * set to generate code that supports startup of the Flex framework.
         * @param emittedCompilationUnits The {@link Set} of
         * {@link ICompilationUnit}s that any {@link ICompilationUnit}s added to
         * this frame should be added to. This {@link Set} is used to ensure
         * that each {@link ICompilationUnit} is only added to a single frame in
         * a SWF.
         * @param problems {@link Collection} of {@link ICompilerProblem}s that
         * any {@link ICompilerProblem}s from any {@link ICompilationUnit} added
         * to the new {@link SWFFrame}s should be added to.
         * @throws InterruptedException
         */
        protected void createFrames(SWFTarget swfTarget, ISWF swf,
                ImmutableSet<ICompilationUnit> builtCompilationUnits, Set<ICompilationUnit> emittedCompilationUnits,
                Collection<ICompilerProblem> problems) throws InterruptedException {
            for (final SWFFrameInfo frameInfo : frameInfos) {
                SWFFrame swfFrame = createFrame(swfTarget, frameInfo, builtCompilationUnits,
                        emittedCompilationUnits, problems);
                swf.addFrame(swfFrame);
            }
        }
    }

}