/* * * 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.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.Vector; import org.apache.flex.abc.ABCConstants; import org.apache.flex.abc.ABCEmitter; import org.apache.flex.abc.ABCLinker; import org.apache.flex.abc.ABCParser; import org.apache.flex.abc.EntryOrderedStore; import org.apache.flex.abc.ABCEmitter.EmitterClassVisitor; import org.apache.flex.abc.Pool; import org.apache.flex.abc.semantics.Metadata; import org.apache.flex.abc.semantics.MethodInfo; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.semantics.Namespace; import org.apache.flex.abc.semantics.Trait; 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.caches.SWFCache; 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.problems.UnexpectedExceptionProblem; 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.compiler.units.requests.ISWFTagsRequestResult; 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>(); protected boolean isLibrary = false; /** * 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) { ISWFTagsRequestResult swfTags = cu.getSWFTagsRequest().get(); if (targetSettings.allowSubclassOverrides() && !isLibrary) { // scan the ABC in each CU for overrides that need fixing. // the override needs to be put back to the base override // otherwise you will get a verify error at runtime boolean changedABC = false; final DoABCTag doABC = swfTags.getDoABCTag(); ABCParser parser = new ABCParser(doABC.getABCData()); ABCEmitter emitter = new ABCEmitter(); try { parser.parseABC(emitter); Collection<EmitterClassVisitor> classes = emitter.getDefinedClasses(); for (EmitterClassVisitor clazz : classes) { System.out.println("scanning for overrides: " + clazz.getInstanceInfo().name.getBaseName()); Iterator<Trait> instanceTraits = clazz.instanceTraits.iterator(); while (instanceTraits.hasNext()) { Trait trait = instanceTraits.next(); Vector<Metadata> metas = trait.getMetadata(); metas: for (Metadata meta : metas) { if (meta.getName().equals(IMetaAttributeConstants.ATTRIBUTE_SWFOVERRIDE)) { EntryOrderedStore<MethodInfo> methods = emitter.getMethodInfos(); for (MethodInfo method : methods) { String methodName = method.getMethodName(); if (methodName == null) continue; if (methodName.equals(trait.getName().getBaseName())) { String[] keys = meta.getKeys(); int n = keys.length; for (int i = 0; i < n; i++) { if (keys[i].equals(IMetaAttributeConstants.NAME_SWFOVERRIDE_RETURNS)) { String returnString = meta.getValues()[i]; int c = returnString.lastIndexOf("."); String packageName = ""; String baseName = returnString; if (c != -1) { packageName = returnString.substring(0, c); baseName = returnString.substring(c + 1); } Pool<Name> namePool = emitter.getNamePool(); List<Name> nameList = namePool.getValues(); boolean foundName = false; for (Name name : nameList) { String base = name.getBaseName(); if (base == null) continue; Namespace ns = name.getSingleQualifier(); if (ns == null) continue; String nsName = ns.getName(); if (nsName == null) continue; if (base.equals(baseName) && nsName.equals(packageName)) { method.setReturnType(name); foundName = true; changedABC = true; break metas; } } if (!foundName) { Pool<String> stringPool = emitter.getStringPool(); stringPool.add(packageName);// theoretically, it won't be added if already there stringPool.add(baseName); // theoretically, it won't be added if already there Namespace ns = new Namespace(ABCConstants.CONSTANT_PackageNs, packageName); Pool<Namespace> nsPool = emitter.getNamespacePool(); nsPool.add(ns); Name name = new Name(ns, baseName); namePool.add(name); method.setReturnType(name); changedABC = true; break metas; } } else if (keys[i].equals(IMetaAttributeConstants.NAME_SWFOVERRIDE_PARAMS)) { String paramList = meta.getValues()[i]; String[] parts; if (paramList.contains(",")) parts = paramList.split(","); else { parts = new String[1]; parts[0] = paramList; } Vector<Name> newList = new Vector<Name>(); for (String part : parts) { int c = part.lastIndexOf("."); String packageName = ""; String baseName = part; if (c != -1) { packageName = part.substring(0, c); baseName = part.substring(c + 1); } Pool<Name> namePool = emitter.getNamePool(); List<Name> nameList = namePool.getValues(); boolean foundName = false; for (Name name : nameList) { String base = name.getBaseName(); if (base == null) continue; Namespace ns = name.getSingleQualifier(); if (ns == null) continue; String nsName = ns.getName(); if (nsName == null) continue; if (base.equals(baseName) && nsName.equals(packageName)) { newList.add(name); foundName = true; changedABC = true; break; } } if (!foundName) { Pool<String> stringPool = emitter.getStringPool(); stringPool.add(packageName);// theoretically, it won't be added if already there stringPool.add(baseName); // theoretically, it won't be added if already there Namespace ns = new Namespace(ABCConstants.CONSTANT_PackageNs, packageName); Pool<Namespace> nsPool = emitter.getNamespacePool(); nsPool.add(ns); Name name = new Name(ns, baseName); namePool.add(name); newList.add(name); changedABC = true; } } method.setParamTypes(newList); break metas; } } } } } } } } } catch (Exception ee) {} if (changedABC) { try { doABC.setABCData(emitter.emit()); } catch (Exception e) { reportProblem(new UnexpectedExceptionProblem(e)); return false; } } } boolean tagsAdded = swfTags.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); } } } }