blob: 1d62143da62e76343e89d4d68735a0e99072f3b4 [file] [log] [blame]
/*
*
* 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.royale.compiler.internal.units;
import java.io.FileNotFoundException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.royale.compiler.internal.as.codegen.CodeGeneratorManager;
import org.apache.commons.io.FilenameUtils;
import org.apache.royale.compiler.common.DependencyType;
import org.apache.royale.compiler.common.Multiname;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.definitions.references.IReference;
import org.apache.royale.compiler.definitions.references.ReferenceFactory;
import org.apache.royale.compiler.filespecs.IFileSpecification;
import org.apache.royale.compiler.fxg.dom.IFXGNode;
import org.apache.royale.compiler.fxg.flex.FXGSymbolClass;
import org.apache.royale.compiler.fxg.flex.FlexFXG2SWFTranscoder;
import org.apache.royale.compiler.internal.definitions.ClassDefinition;
import org.apache.royale.compiler.internal.definitions.PackageDefinition;
import org.apache.royale.compiler.internal.fxg.resources.FXGFileResolver;
import org.apache.royale.compiler.internal.fxg.sax.FXGSAXParser;
import org.apache.royale.compiler.internal.parsing.as.ASParser;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.internal.projects.DefinitionPriority.BasePriority;
import org.apache.royale.compiler.internal.scopes.ASProjectScope;
import org.apache.royale.compiler.internal.scopes.FXGFileScope;
import org.apache.royale.compiler.internal.scopes.PackageScope;
import org.apache.royale.compiler.internal.scopes.TypeScope;
import org.apache.royale.compiler.internal.semantics.PostProcessStep;
import org.apache.royale.compiler.internal.targets.TagSorter;
import org.apache.royale.compiler.internal.tree.as.FileNode;
import org.apache.royale.compiler.internal.units.requests.ABCBytesRequestResult;
import org.apache.royale.compiler.internal.units.requests.ASFileScopeRequestResult;
import org.apache.royale.compiler.internal.units.requests.SyntaxTreeRequestResult;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.InternalCompilerProblem2;
import org.apache.royale.compiler.units.requests.IABCBytesRequestResult;
import org.apache.royale.compiler.units.requests.IFileScopeRequestResult;
import org.apache.royale.compiler.units.requests.IOutgoingDependenciesRequestResult;
import org.apache.royale.compiler.units.requests.ISWFTagsRequestResult;
import org.apache.royale.compiler.units.requests.ISyntaxTreeRequestResult;
import org.apache.royale.swf.SWFFrame;
import org.apache.royale.swf.tags.DoABCTag;
import org.apache.royale.swf.tags.ICharacterTag;
import org.apache.royale.swf.tags.ITag;
import org.apache.royale.utils.FilenameNormalization;
import com.google.common.collect.Iterables;
/**
* This is a Compilation Unit which handles FXG compilation
*/
public class FXGCompilationUnit extends CompilationUnitBase
{
public static String fxgBaseClassName = "spark.core.SpriteVisualElement";
private String qname;
private static final String SUB_SYSTEM = "FXGCompilationUnit"; // used for error reporting
/**
* A {@code IFileSpecification} implementation from {@code String}.
*/
private class GeneratedSourceFileSpecfication implements IFileSpecification
{
public GeneratedSourceFileSpecfication(String name, String content)
{
this.reader = new StringReader(content);
this.name = FilenameNormalization.normalize(name);
}
private final StringReader reader;
private final String name;
@Override
public String getPath()
{
return name;
}
@Override
public Reader createReader() throws FileNotFoundException
{
return reader;
}
@Override
public long getLastModified()
{
return 0;
}
@Override
public boolean isOpenDocument()
{
return false;
}
}
public FXGCompilationUnit(CompilerProject project, String path, BasePriority basePriority, String qname)
{
super(project, path, basePriority, qname);
this.qname = qname;
}
@Override
public UnitType getCompilationUnitType()
{
return UnitType.FXG_UNIT;
}
@Override
protected ISyntaxTreeRequestResult handleSyntaxTreeRequest() throws InterruptedException
{
startProfile(Operation.GET_SYNTAX_TREE);
getProject().clearScopeCacheForCompilationUnit(this);
final Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
IFXGNode fxgroot = null;
try
{
fxgroot = new FXGSAXParser().parse(getRootFileSpecification().createReader(), qname, problems);
}
catch(Exception ex)
{
ex.printStackTrace();
problems.add(new InternalCompilerProblem2(getAbsoluteFilename(), ex, SUB_SYSTEM));
}
finally
{
stopProfile(Operation.GET_SYNTAX_TREE);
}
return new FXGSyntaxTreeRequestResult(fxgroot, getRootFileSpecification().getLastModified(), problems);
}
/**
* Syntax Tree request result for FXG files. FXG files are not represented
* by a traditional AST, they use their own tree and this class allows
* {@link FXGCompilationUnit} to cache an FXG tree result.
*/
private static class FXGSyntaxTreeRequestResult extends SyntaxTreeRequestResult {
private IFXGNode rootNode;
public FXGSyntaxTreeRequestResult(IFXGNode rootNode, long lastModified, Collection<ICompilerProblem> problems)
{
super(lastModified, problems);
this.rootNode = rootNode;
}
/**
* Returns the root node for the FXG file handled
* by this compilation unit
*
* @return root node
*/
public IFXGNode getRootNode()
{
return rootNode;
}
}
/**
* This function will create a temporary filscope. Please note that this will not be the filescope that is used in the actual AST
*/
@Override
protected IFileScopeRequestResult handleFileScopeRequest() throws InterruptedException
{
startProfile(Operation.GET_FILESCOPE);
try
{
List<ICompilerProblem> noProblems = Collections.emptyList();
IFileSpecification rootFileSpec = getRootFileSpecification();
FXGFileScope fileScope = createFileScope();
return new ASFileScopeRequestResult(getDefinitionPromises(), getDefinitionPriority(), noProblems, fileScope, rootFileSpec);
}
finally
{
stopProfile(Operation.GET_FILESCOPE);
}
}
@Override
protected IOutgoingDependenciesRequestResult handleOutgoingDependenciesRequest () throws InterruptedException
{
startProfile(Operation.GET_SEMANTIC_PROBLEMS);
final Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
Map<ITag, ITag> extraTags = new HashMap<ITag, ITag>();
FXGSymbolClass symbolClass = null;
FileNode fileNode = null;
ASProjectScope projectScope = getProject().getScope();
try
{
FXGSyntaxTreeRequestResult syntaxTreeResult = (FXGSyntaxTreeRequestResult)getSyntaxTreeRequest().get();
IFXGNode tree = syntaxTreeResult.getRootNode();
FlexFXG2SWFTranscoder transcoder = new FlexFXG2SWFTranscoder(getProject());
transcoder.setResourceResolver(new FXGFileResolver(FilenameUtils.getFullPath(getRootFileSpecification().getPath())));
symbolClass = transcoder.transcode(tree,
Multiname.getPackageNameForQName(qname), Multiname.getBaseNameForQName(qname), extraTags, problems);
//Add dependencies to the classes required by the FXG processed by this compilation unit
for (ITypeDefinition definition : transcoder.getDependencies())
{
getProject().addDependency(this, projectScope.getCompilationUnitForDefinition(definition),
DependencyType.EXPRESSION, definition.getQualifiedName());
}
StringBuilder sb = new StringBuilder(symbolClass.getGeneratedSource());
if (symbolClass.getAdditionalSymbolClasses() != null)
{
for (FXGSymbolClass symbol : symbolClass.getAdditionalSymbolClasses())
{
sb.append(symbol.getGeneratedSource());
}
}
IFileSpecification virtualSymbolSource = new GeneratedSourceFileSpecfication(qname, sb.toString());
fileNode = ASParser.parseFile(virtualSymbolSource, getProject().getWorkspace());
fileNode.runPostProcess(EnumSet.of(PostProcessStep.POPULATE_SCOPE));
projectScope.addScopeForCompilationUnit(this, fileNode.getFileScope());
updateEmbedCompilationUnitDependencies(fileNode.getEmbedNodes(), problems);
}
catch (Exception e)
{
e.printStackTrace();
problems.add(new InternalCompilerProblem2(getAbsoluteFilename(), e, SUB_SYSTEM));
}
finally
{
stopProfile(Operation.GET_SEMANTIC_PROBLEMS);
}
return new FXGOutgoingDependenciesResult(fileNode, symbolClass, extraTags, problems);
}
/**
* Semantic problems result for FXG files.
*/
private static class FXGOutgoingDependenciesResult implements IOutgoingDependenciesRequestResult
{
private Collection<ICompilerProblem> problems;
protected FileNode fileNode;
protected FXGSymbolClass symbolClass;
protected Map<ITag, ITag> extraTags;
public FXGOutgoingDependenciesResult (FileNode fileNode, FXGSymbolClass symbolClass,
Map<ITag, ITag> extraTags, Collection<ICompilerProblem> problems)
{
this.problems = problems;
this.fileNode = fileNode;
this.symbolClass = symbolClass;
this.extraTags = extraTags;
}
@Override
public ICompilerProblem[] getProblems()
{
return problems.toArray(new ICompilerProblem[0]);
}
}
@Override
protected IABCBytesRequestResult handleABCBytesRequest() throws InterruptedException
{
FXGOutgoingDependenciesResult semanticResults = (FXGOutgoingDependenciesResult) getOutgoingDependenciesRequest().get();
startProfile(Operation.GET_ABC_BYTES);
try
{
byte abc[] = CodeGeneratorManager.getCodeGenerator().generate(getFilenameNoPath(), semanticResults.fileNode, getProject()).getABCBytes();
return new ABCBytesRequestResult(abc);
}
finally
{
stopProfile(Operation.GET_ABC_BYTES);
}
}
@Override
protected ISWFTagsRequestResult handleSWFTagsRequest() throws InterruptedException
{
FXGOutgoingDependenciesResult semanticResults = (FXGOutgoingDependenciesResult) getOutgoingDependenciesRequest().get();
IABCBytesRequestResult byteResult = getABCBytesRequest().get();
startProfile(Operation.GET_SWF_TAGS);
final FXGSymbolClass symbolClass = semanticResults.symbolClass;
final Map<ITag, ITag> extraTags = semanticResults.extraTags;
final Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
final DoABCTag abcTag = new DoABCTag();
try
{
abcTag.setName(qname);
abcTag.setABCData(byteResult.getABCBytes());
}
catch (Exception e)
{
ICompilerProblem problem = new InternalCompilerProblem2(getRootFileSpecification().getPath(), e, SUB_SYSTEM);
problems.add(problem);
}
finally
{
stopProfile(Operation.GET_SWF_TAGS);
}
return new ISWFTagsRequestResult()
{
@Override
public boolean addToFrame(SWFFrame frame)
{
ICharacterTag symbolTag = symbolClass.getSymbol();
List<ITag> symbolTags = TagSorter.sortFullGraph(Collections.singletonList((ITag)symbolTag));
for (ITag tag : symbolTags)
{
frame.addTag(tag);
if (extraTags.containsKey(tag))
{
frame.addTag(extraTags.get(tag));
}
}
if (symbolClass.getAdditionalSymbolClasses() != null )
{
for (FXGSymbolClass symbol : symbolClass.getAdditionalSymbolClasses())
{
frame.defineSymbol(symbol.getSymbol(), symbol.getQualifiedClassName());
}
}
frame.addTag(abcTag);
frame.defineSymbol(symbolClass.getSymbol(), qname);
return true;
}
@Override
public ICompilerProblem[] getProblems()
{
return problems.toArray(new ICompilerProblem[0]);
}
@Override
public String getDoABCTagName()
{
return abcTag.getName();
}
@Override
public DoABCTag getDoABCTag()
{
return abcTag;
}
};
}
/**
* Creates an empty FileScope for purposes of definition reporting
*
* @return a {@link FXGFileScope} with a class definition for the FXG source
*/
private FXGFileScope createFileScope()
{
FXGFileScope fileScope = new FXGFileScope(this, getAbsoluteFilename());
String packageName = Multiname.getPackageNameForQName(qname);
PackageScope packageScope = new PackageScope(fileScope, packageName);
packageScope.setContainingScope(fileScope);
PackageDefinition packageDefinition = new PackageDefinition(packageName);
packageDefinition.setContainedScope(packageScope);
fileScope.addDefinition(packageDefinition);
Multiname mname = Multiname.crackDottedQName(getProject(), qname);
INamespaceDefinition packageNS = Iterables.getOnlyElement(mname.getNamespaceSet());
ClassDefinition classDefinition = new ClassDefinition(mname.getBaseName(), (INamespaceReference)packageNS);
IReference baseClass = ReferenceFactory.packageQualifiedReference(getProject().getWorkspace(), fxgBaseClassName);
classDefinition.setBaseClassReference(baseClass);
TypeScope classScope = new TypeScope(packageScope, classDefinition);
classScope.setContainingDefinition(classDefinition);
classDefinition.setContainedScope(classScope);
classDefinition.setupThisAndSuper();
packageScope.addDefinition(classDefinition);
return fileScope;
}
}