| /* |
| * |
| * 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.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.royale.compiler.common.DependencyType; |
| import org.apache.royale.compiler.common.Multiname; |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.filespecs.IFileSpecification; |
| import org.apache.royale.compiler.internal.abc.ABCScopeBuilder; |
| import org.apache.royale.compiler.internal.as.codegen.CodeGeneratorManager; |
| import org.apache.royale.compiler.internal.definitions.ClassDefinition; |
| import org.apache.royale.compiler.embedding.EmbedAttribute; |
| import org.apache.royale.compiler.embedding.IEmbedData; |
| import org.apache.royale.compiler.internal.embedding.EmbedData; |
| import org.apache.royale.compiler.internal.embedding.transcoders.SkinTranscoder; |
| import org.apache.royale.compiler.internal.embedding.transcoders.TranscoderBase; |
| import org.apache.royale.compiler.internal.projects.CompilerProject; |
| import org.apache.royale.compiler.internal.projects.DefinitionPriority; |
| import org.apache.royale.compiler.internal.scopes.ASFileScope; |
| import org.apache.royale.compiler.internal.scopes.ASFileScopeProvider; |
| import org.apache.royale.compiler.internal.scopes.ASProjectScope; |
| 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.ABCFileScopeRequestResult; |
| import org.apache.royale.compiler.internal.units.requests.EmbedFileScopeRequestResult; |
| import org.apache.royale.compiler.internal.units.requests.SWFTagsRequestResult; |
| import org.apache.royale.compiler.internal.units.requests.SyntaxTreeRequestResult; |
| import org.apache.royale.compiler.problems.ICompilerProblem; |
| import org.apache.royale.compiler.problems.InvalidABCByteCodeProblem; |
| import org.apache.royale.compiler.problems.NoScopesInABCCompilationUnitProblem; |
| import org.apache.royale.compiler.scopes.IASScope; |
| import org.apache.royale.compiler.tree.as.IASNode; |
| import org.apache.royale.compiler.units.ICompilationUnit; |
| 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.swc.ISWCFileEntry; |
| |
| /** |
| * This is a Compilation Unit which handles transcoding and embedding of embed |
| * meta data. |
| */ |
| public class EmbedCompilationUnit extends CompilationUnitBase |
| { |
| private static String getSourcePath(EmbedData data) |
| { |
| ISWCFileEntry swcFile = data.getSWCSource(); |
| if (swcFile != null) |
| return swcFile.getContainingSWCPath(); |
| else |
| return (String)data.getAttribute(EmbedAttribute.SOURCE); |
| } |
| |
| public EmbedCompilationUnit(CompilerProject project, EmbedData data) |
| { |
| super(project, getSourcePath(data), DefinitionPriority.BasePriority.SOURCE_LIST, true); |
| this.embedData = data; |
| } |
| |
| private final EmbedData embedData; |
| |
| @Override |
| public UnitType getCompilationUnitType() |
| { |
| return UnitType.EMBED_UNIT; |
| } |
| |
| @Override |
| public String getName() |
| { |
| return embedData.getQName(); |
| } |
| |
| @Override |
| protected ISyntaxTreeRequestResult handleSyntaxTreeRequest() throws InterruptedException |
| { |
| startProfile(Operation.GET_SYNTAX_TREE); |
| try |
| { |
| getProject().clearScopeCacheForCompilationUnit(this); |
| TranscoderBase transcoder = (TranscoderBase)embedData.getTranscoder(); |
| if (transcoder instanceof SkinTranscoder) |
| { |
| List<ICompilerProblem> noProblems = Collections.emptyList(); |
| return new SyntaxTreeRequestResult(getRootFileSpecification().getLastModified(), noProblems); |
| } |
| else |
| { |
| Collection<ICompilerProblem> problems = new LinkedList<ICompilerProblem>(); |
| FileNode fileNode = transcoder.buildAST(problems, getAbsoluteFilename()); |
| final ASFileScope fileScope = fileNode.getFileScope(); |
| addScopeToProjectScope(new ASFileScope[] { fileScope }); |
| markClassAsEmbed(fileScope); |
| |
| return new SyntaxTreeRequestResult(fileNode, fileNode.getIncludeHandler().getIncludedFiles(), getRootFileSpecification().getLastModified(), problems); |
| } |
| } |
| finally |
| { |
| stopProfile(Operation.GET_SYNTAX_TREE); |
| } |
| } |
| |
| @Override |
| protected void verifyAST(IASNode ast) |
| { |
| // Don't try to verify ASTs produced by an EmbedCompilationUnit |
| // because none of the nodes have source location info. |
| } |
| |
| @Override |
| protected IFileScopeRequestResult handleFileScopeRequest() throws InterruptedException |
| { |
| TranscoderBase transcoder = (TranscoderBase)embedData.getTranscoder(); |
| // SkinTranscoder generates ABC directly rather than an AST, so handle differently |
| // Eventually all transcoders should be updated to go directly to ABC |
| if (transcoder instanceof SkinTranscoder) |
| { |
| List<IASScope> scopeList = null; |
| List<ICompilerProblem> problems = new LinkedList<ICompilerProblem>(); |
| IFileSpecification rootSource = getRootFileSpecification(); |
| try |
| { |
| IABCBytesRequestResult abcResult = getABCBytesRequest().get(); |
| |
| startProfile(Operation.GET_FILESCOPE); |
| |
| String path = rootSource.getPath(); |
| ABCScopeBuilder abcScopeBuilder = new ABCScopeBuilder( |
| getProject().getWorkspace(), |
| abcResult.getABCBytes(), |
| path, |
| ASFileScopeProvider.getInstance()); |
| scopeList = abcScopeBuilder.build(); |
| if (scopeList.isEmpty()) |
| { |
| problems.add(new NoScopesInABCCompilationUnitProblem(path)); |
| } |
| for (IASScope scope : scopeList) |
| markClassAsEmbed((ASFileScope)scope); |
| |
| return new ABCFileScopeRequestResult(problems, scopeList); |
| } |
| catch (Exception e) |
| { |
| ICompilerProblem problem = new InvalidABCByteCodeProblem(rootSource.getPath()); |
| problems.add(problem); |
| return new ABCFileScopeRequestResult(problems, Collections.<IASScope> emptyList()); |
| } |
| finally |
| { |
| stopProfile(Operation.GET_FILESCOPE); |
| } |
| } |
| else |
| { |
| ISyntaxTreeRequestResult syntaxTreeResult = getSyntaxTreeRequest().get(); |
| FileNode rootNode = (FileNode)syntaxTreeResult.getAST(); |
| if (rootNode == null) |
| { |
| return new EmbedFileScopeRequestResult(null); |
| } |
| |
| startProfile(Operation.GET_FILESCOPE); |
| try |
| { |
| final IASScope fileScope = rootNode.getScope(); |
| assert fileScope instanceof ASFileScope : "Expect ASFileScope as the top-level scope, but found " + fileScope.getClass(); |
| return new EmbedFileScopeRequestResult((ASFileScope)fileScope); |
| } |
| finally |
| { |
| stopProfile(Operation.GET_FILESCOPE); |
| } |
| } |
| } |
| |
| @Override |
| protected IABCBytesRequestResult handleABCBytesRequest() throws InterruptedException |
| { |
| TranscoderBase transcoder = (TranscoderBase)embedData.getTranscoder(); |
| // SkinTranscoder generates ABC directly rather than an AST, so handle differently |
| // Eventually all transcoders should be updated to go directly to ABC |
| if (transcoder instanceof SkinTranscoder) |
| { |
| startProfile(Operation.GET_ABC_BYTES); |
| try |
| { |
| List<ICompilerProblem> problems = new LinkedList<ICompilerProblem>(); |
| byte[] bytes = transcoder.buildABC(getProject(), problems); |
| ICompilerProblem[] problemsArray = problems.toArray(new ICompilerProblem[problems.size()]); |
| Set<IEmbedData> iembeds = new HashSet<IEmbedData>(); |
| iembeds.add(embedData); |
| return new ABCBytesRequestResult(bytes, problemsArray, iembeds); |
| } |
| finally |
| { |
| stopProfile(Operation.GET_ABC_BYTES); |
| } |
| } |
| else |
| { |
| |
| ISyntaxTreeRequestResult syntaxTreeResult = getSyntaxTreeRequest().get(); |
| IASNode rootNode = syntaxTreeResult.getAST(); |
| if (rootNode == null) |
| { |
| return new ABCBytesRequestResult(syntaxTreeResult.getProblems()); |
| } |
| |
| startProfile(Operation.GET_ABC_BYTES); |
| try |
| { |
| return CodeGeneratorManager.getCodeGenerator().generate(getFilenameNoPath(), rootNode, getProject()); |
| } |
| finally |
| { |
| stopProfile(Operation.GET_ABC_BYTES); |
| } |
| } |
| } |
| |
| @Override |
| protected ISWFTagsRequestResult handleSWFTagsRequest() throws InterruptedException |
| { |
| final IABCBytesRequestResult abc = getABCBytesRequest().get(); |
| |
| startProfile(Operation.GET_SWF_TAGS); |
| |
| try |
| { |
| String tagName; |
| String qname = getName(); |
| if (qname == null || "".equals(qname)) |
| tagName = DEFAULT_DO_ABC_TAG_NAME; |
| else |
| tagName = qname.replace('.', '/'); |
| |
| Collection<IEmbedData> embeds = abc.getEmbeds(); |
| if (embeds.size() == 0) |
| embeds = Collections.singleton((IEmbedData)embedData); |
| return new SWFTagsRequestResult(abc.getABCBytes(), tagName, embeds); |
| } |
| finally |
| { |
| stopProfile(Operation.GET_SWF_TAGS); |
| } |
| } |
| |
| @Override |
| protected IOutgoingDependenciesRequestResult handleOutgoingDependenciesRequest () throws InterruptedException |
| { |
| startProfile(Operation.GET_SEMANTIC_PROBLEMS); |
| |
| try |
| { |
| Collection<ICompilerProblem> problems = new LinkedList<ICompilerProblem>(); |
| analyze(problems); |
| |
| IOutgoingDependenciesRequestResult result = new IOutgoingDependenciesRequestResult() |
| { |
| @Override |
| public ICompilerProblem[] getProblems() |
| { |
| return IOutgoingDependenciesRequestResult.NO_PROBLEMS; |
| } |
| }; |
| |
| return result; |
| } |
| finally |
| { |
| stopProfile(Operation.GET_SEMANTIC_PROBLEMS); |
| } |
| } |
| |
| public EmbedData getEmbedData() |
| { |
| return embedData; |
| } |
| |
| private void analyze(Collection<ICompilerProblem> problems) |
| { |
| // if this class extends another, add a dependency on this compilation unit to the class that it extends |
| if (embedData.generatedClassExtendsAnother()) |
| { |
| ASProjectScope projectScope = getProject().getScope(); |
| IDefinition[] defs = projectScope.findAllDefinitionsByName(Multiname.crackDottedQName(getProject(), embedData.getTranscoder().getBaseClassQName())); |
| ICompilationUnit referencedCU = projectScope.getCompilationUnitForScope(defs[0].getContainingScope()); |
| getProject().addDependency(this, referencedCU, DependencyType.INHERITANCE, defs[0].getQualifiedName()); |
| } |
| } |
| |
| /** |
| * Marks all the externally visible classes in the scope |
| * as being embed classes. |
| */ |
| private void markClassAsEmbed(ASFileScope fileScope) |
| { |
| // Mark all the clases as being embed classes. |
| Collection<IDefinition> definitions = new LinkedList<IDefinition>(); |
| fileScope.collectExternallyVisibleDefinitions(definitions, false); |
| for (IDefinition definition : definitions) |
| { |
| if (definition instanceof ClassDefinition) |
| { |
| ClassDefinition classDefinition = (ClassDefinition)definition; |
| classDefinition.setGeneratedEmbedClass(); |
| classDefinition.setExcludedClass(); |
| } |
| } |
| } |
| } |