/* * * 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.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.abc.ABCConstants; import org.apache.flex.abc.ABCLinker; import org.apache.flex.abc.ABCLinker.ABCLinkerSettings; 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.internal.config.FrameInfo; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.problems.FileNotFoundProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.ImproperlyConfiguredTargetProblem; import org.apache.flex.compiler.problems.UnableToFindRootClassDefinitionProblem; import org.apache.flex.compiler.targets.ITarget; import org.apache.flex.compiler.targets.ITargetProgressMonitor; import org.apache.flex.compiler.targets.ITargetSettings; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IFileNode; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.compiler.units.requests.IRequest; import org.apache.flex.compiler.units.requests.ISyntaxTreeRequestResult; import org.apache.flex.swf.ISWF; import org.apache.flex.swf.SWFFrame; import org.apache.flex.swf.tags.DoABCTag; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; /** * Sub-class of {@link SWFTarget} that builds a SWF that is meant to be loaded * by the flash player ( as opposed to a library.swf in a SWC ). */ public class AppSWFTarget extends SWFTarget { /** * Constructor * * @param project {@link CompilerProject} that contains the code this * {@link AppSWFTarget} compiles. * @param targetSettings {@link ITargetSettings} that contains the * confuration of this {@link AppSWFTarget}. * @param progressMonitor {@link ITargetProgressMonitor} to which status is * reported as this {@link AppSWFTarget} is built. */ public AppSWFTarget(CompilerProject project, ITargetSettings targetSettings, ITargetProgressMonitor progressMonitor) { this(project, targetSettings, progressMonitor, Collections.<ICompilationUnit>emptySet()); } /** * Constructor * * @param project {@link CompilerProject} that contains the code this * {@link AppSWFTarget} compiles. * @param targetSettings {@link ITargetSettings} that contains the * confuration of this {@link AppSWFTarget}. * @param progressMonitor {@link ITargetProgressMonitor} to which status is * reported as this {@link AppSWFTarget} is built. * @param additionalRootedCompilationUnits {@link Set} of additional * {@link ICompilationUnit}s that will be built add added to the output of * this {@link AppSWFTarget}. */ public AppSWFTarget(CompilerProject project, ITargetSettings targetSettings, ITargetProgressMonitor progressMonitor, Set<ICompilationUnit> additionalRootedCompilationUnits) { super(project, targetSettings, progressMonitor); assert additionalRootedCompilationUnits != null; this.additionalRootedCompilationUnits = additionalRootedCompilationUnits; } private static final FramesInformation NO_EXPLICIT_FRAMES = new FramesInformation(Collections.<SWFFrameInfo>emptyList()); /** * {@link Set} of additional {@link ICompilationUnit}s that will be built * add added to the output of this {@link AppSWFTarget}. */ private final Set<ICompilationUnit> additionalRootedCompilationUnits; /** * List of {@link SWFFrameInfo}s for frames explicitly requested by the * {@link ITargetSettings} for this {@link ITarget}. * * @see ITargetSettings#getFrameLabels() */ private FramesInformation swfFrameInfosForExplicitFrames; /** * Cached reference to the {@link ClassDefinition} for the root class. */ private ClassDefinition rootClassDefinition; @Override protected Iterable<ICompilerProblem> computeFatalProblems() throws InterruptedException { final Iterable<ICompilerProblem> fatalProblemsFromSuper = super.computeFatalProblems(); if (!Iterables.isEmpty(fatalProblemsFromSuper)) return fatalProblemsFromSuper; IResolvedQualifiersReference rootClassRef = getRootClassReference(); if (rootClassRef == null) return ImmutableList.<ICompilerProblem>of(new ImproperlyConfiguredTargetProblem()); String rootClassFileName = targetSettings.getRootSourceFileName(); if (rootClassFileName == null) return ImmutableList.<ICompilerProblem>of(new ImproperlyConfiguredTargetProblem()); Collection<ICompilationUnit> rootClassCompilationUnits = project.getCompilationUnits(rootClassFileName); assert rootClassCompilationUnits.isEmpty() || rootClassCompilationUnits.size() == 1; if (rootClassCompilationUnits.isEmpty()) return ImmutableList.<ICompilerProblem>of(new FileNotFoundProblem(rootClassFileName)); assert Iterables.getOnlyElement(rootClassCompilationUnits) != null : "The build should have been aborted before this point if there is no root class compilation unit."; IDefinition rootClassDefinition = rootClassRef.resolve(project); if (rootClassDefinition == null) return ImmutableList.<ICompilerProblem>of(new UnableToFindRootClassDefinitionProblem(targetSettings.getRootClassName())); return ImmutableList.<ICompilerProblem>of(); } /** * Builds a {@link FramesInformation} object for any explicit * frames specified by {@link ITargetSettings#getFrameLabels()}. * @return A new {@link FramesInformation} */ private FramesInformation computeExplicitFramesInformation() { List<FrameInfo> explicitFrames = targetSettings.getFrameLabels(); if (explicitFrames.isEmpty()) { return NO_EXPLICIT_FRAMES; } ArrayList<SWFFrameInfo> frames = new ArrayList<SWFFrameInfo>(explicitFrames.size()); for (FrameInfo frameInfo : explicitFrames) { ImmutableList.Builder<ICompilerProblem> problems = ImmutableList.builder(); List<String> classes = frameInfo.getFrameClasses(); List<ClassDefinition> resolvedClasses = new ArrayList<ClassDefinition>(classes.size()); Set<ICompilationUnit> frameCompilationUnits = new HashSet<ICompilationUnit>(classes.size()); for (String frameClass : classes) { IResolvedQualifiersReference ref = ReferenceFactory.packageQualifiedReference(project.getWorkspace(), frameClass); IDefinition def = ref.resolve(project); if (def instanceof ClassDefinition) { resolvedClasses.add((ClassDefinition)def); ICompilationUnit defCU = project.getScope().getCompilationUnitForDefinition(def); assert (defCU != null) : "could not resolve def to CU"; frameCompilationUnits.add(defCU); } else { // TODO add problem!!! http://bugs.adobe.com/jira/browse/CMP-2059 } } frames.add(new SWFFrameInfo(frameInfo.getLabel(), SWFFrameInfo.EXTERNS_ALLOWED, frameCompilationUnits, problems.build())); } return new FramesInformation(frames); } /** * Get's a cached {@code FramesInformation} for frames created by the * -frames command line option. * * @return a cached {@code FramesInformation} for frames created by the * -frames command line option. */ protected final FramesInformation getExplicitFramesInformation() { if (swfFrameInfosForExplicitFrames != null) return swfFrameInfosForExplicitFrames; swfFrameInfosForExplicitFrames = computeExplicitFramesInformation(); return swfFrameInfosForExplicitFrames; } @Override protected ITargetAttributes computeTargetAttributes() throws InterruptedException { ICompilationUnit mainUnit = getRootClassCompilationUnit(); IRequest<ISyntaxTreeRequestResult, ICompilationUnit> request = mainUnit.getSyntaxTreeRequest(); ISyntaxTreeRequestResult result = request.get(); IASNode root = result.getAST(); if (!(root instanceof IFileNode)) return NilTargetAttributes.INSTANCE; final ITargetAttributes nodeTargetAttributes = ((IFileNode)root).getTargetAttributes(this.project); if (nodeTargetAttributes == null) return NilTargetAttributes.INSTANCE; return nodeTargetAttributes; } /** * Creates a {@link SWFFrameInfo} for the main frame. * * @return A new {@link SWFFrameInfo}. * @throws InterruptedException */ private SWFFrameInfo createMainFrameInfo() throws InterruptedException { final ImmutableSet.Builder<ICompilationUnit> compilationUnits = ImmutableSet.<ICompilationUnit>builder(); ICompilationUnit rootCU = getRootClassCompilationUnit(); compilationUnits.add(rootCU); final Iterable<ICompilationUnit> includesCompilationUnits = getIncludesCompilationUnits(); compilationUnits.addAll(includesCompilationUnits); final Iterable<ICompilationUnit> includeLibrariesCompilationUnits = getIncludeLibrariesCompilationUnits(); compilationUnits.addAll(includeLibrariesCompilationUnits); compilationUnits.addAll(additionalRootedCompilationUnits); Collection<ICompilerProblem> externallyVisibleDefinitionProblems = rootCU.getFileScopeRequest().get().checkExternallyVisibleDefinitions(targetSettings.getRootClassName()); final SWFFrameInfo mainFrameInfo = new SWFFrameInfo(compilationUnits.build(), externallyVisibleDefinitionProblems); return mainFrameInfo; } /** * Create the {@code FramesInformation} which contains the skeleton for the frames * of this SWF. The actual frames will be create in doCreateFrames(). * @throws InterruptedException */ protected FramesInformation computeFramesInformation() throws InterruptedException { final SWFFrameInfo mainFrameInfo = createMainFrameInfo(); final FramesInformation explicitFrames = getExplicitFramesInformation(); Iterable<SWFFrameInfo> frames = Iterables.concat(Collections.singletonList(mainFrameInfo), explicitFrames.frameInfos); return new AppFramesInformation(frames, targetSettings.getRootClassName()); } @Override protected void addLinkedABCToFrame(SWFFrame targetFrame, Iterable<DoABCTag> inputABCs, ABCLinkerSettings linkSettings) throws Exception { Iterable<byte[]> inputABCsBytes = Iterables.transform(inputABCs, new Function<DoABCTag, byte[]>() { @Override public byte[] apply(DoABCTag arg0) { return arg0.getABCData(); }}); byte[] linkedBytes = ABCLinker.linkABC(inputABCsBytes, ABCConstants.VERSION_ABC_MAJOR_FP10, ABCConstants.VERSION_ABC_MINOR_FP10, linkSettings); DoABCTag linkedTag = new DoABCTag(1, "merged", linkedBytes); targetFrame.addTag(linkedTag); } @Override protected void setKeepAS3MetadataLinkerSetting(ABCLinkerSettings linkSettings) { ITargetSettings settings = getTargetSettings(); 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; } linkSettings.setKeepMetadata(metadataNames); } /** * @return A {@link ClassDefinition} of the root class of this target if there * is one, null otherwise. */ private ClassDefinition computeRootClassDefinition() { IResolvedQualifiersReference rootClassRef = getRootClassReference(); if (rootClassRef == null) return null; IDefinition rootClassDef = rootClassRef.resolve(project); if (!(rootClassDef instanceof ClassDefinition)) return null; return (ClassDefinition)rootClassDef; } protected ClassDefinition getRootClassDefinition() { if (rootClassDefinition != null) return rootClassDefinition; rootClassDefinition = computeRootClassDefinition(); return rootClassDefinition; } /** * @return the root class compilation unit. may be null if the root class * has not been specified */ protected final ICompilationUnit getRootClassCompilationUnit() { String rootClassFileName = targetSettings.getRootSourceFileName(); if (rootClassFileName == null) return null; Collection<ICompilationUnit> rootClassCompilationUnits = project.getCompilationUnits(rootClassFileName); assert rootClassCompilationUnits.size() == 1 : "There must only be a single compilation unit for the root source file!"; return Iterables.getOnlyElement(rootClassCompilationUnits); } /** * @return A {@link IResolvedQualifiersReference} that resolves to the root * class of this target if there is one, null otherwise. */ private IResolvedQualifiersReference getRootClassReference() { String rootClassName = targetSettings.getRootClassName(); if (rootClassName == null) return null; return ReferenceFactory.packageQualifiedReference(project.getWorkspace(), rootClassName, true); } @Override protected ISWF linkSWF(ISWF unLinked) { if (!targetSettings.isOptimized()) return unLinked; return super.linkSWF(unLinked); } private static final class AppFramesInformation extends FramesInformation { AppFramesInformation(Iterable<SWFFrameInfo> frameInfos, String rootClassName) { super(frameInfos); this.rootClassName = rootClassName; } private final String rootClassName; @Override protected void createFrames(SWFTarget swfTarget, ISWF swf, ImmutableSet<ICompilationUnit> builtCompilationUnits, Set<ICompilationUnit> emittedCompilationUnits, Collection<ICompilerProblem> problems) throws InterruptedException { super.createFrames(swfTarget, swf, builtCompilationUnits, emittedCompilationUnits, problems); swf.setTopLevelClass(rootClassName); } } }