| /* |
| * |
| * 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.codegen.js; |
| |
| import java.io.FilterWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Stack; |
| |
| import org.apache.flex.compiler.codegen.IEmitter; |
| import org.apache.flex.compiler.codegen.ISubEmitter; |
| import org.apache.flex.compiler.codegen.js.IJSEmitter; |
| import org.apache.flex.compiler.codegen.js.IMappingEmitter; |
| import org.apache.flex.compiler.common.ASModifier; |
| import org.apache.flex.compiler.common.ISourceLocation; |
| import org.apache.flex.compiler.definitions.IDefinition; |
| import org.apache.flex.compiler.definitions.ITypeDefinition; |
| import org.apache.flex.compiler.internal.codegen.as.ASEmitter; |
| import org.apache.flex.compiler.internal.codegen.as.ASEmitterTokens; |
| import org.apache.flex.compiler.internal.codegen.js.jx.BlockCloseEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.BlockOpenEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.CatchEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.DoWhileLoopEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.DynamicAccessEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.ForLoopEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.FunctionCallArgumentsEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.IfEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.IterationFlowEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.LanguageIdentifierEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.LiteralContainerEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.MemberKeywordEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.NumericLiteralEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.ObjectLiteralValuePairEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.ParameterEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.ParametersEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.ReturnEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.SourceMapDirectiveEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.StatementEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.SwitchEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.TernaryOperatorEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.ThrowEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.TryEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.UnaryOperatorEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.WhileLoopEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.jx.WithEmitter; |
| import org.apache.flex.compiler.internal.codegen.js.utils.EmitterUtils; |
| import org.apache.flex.compiler.internal.tree.as.FunctionNode; |
| import org.apache.flex.compiler.tree.as.IASNode; |
| import org.apache.flex.compiler.tree.as.ICatchNode; |
| import org.apache.flex.compiler.tree.as.IContainerNode; |
| import org.apache.flex.compiler.tree.as.IDefinitionNode; |
| import org.apache.flex.compiler.tree.as.IDynamicAccessNode; |
| import org.apache.flex.compiler.tree.as.IForLoopNode; |
| import org.apache.flex.compiler.tree.as.IFunctionNode; |
| import org.apache.flex.compiler.tree.as.IFunctionObjectNode; |
| import org.apache.flex.compiler.tree.as.IIfNode; |
| import org.apache.flex.compiler.tree.as.IImportNode; |
| import org.apache.flex.compiler.tree.as.IIterationFlowNode; |
| import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode; |
| import org.apache.flex.compiler.tree.as.ILiteralContainerNode; |
| import org.apache.flex.compiler.tree.as.INumericLiteralNode; |
| import org.apache.flex.compiler.tree.as.IObjectLiteralValuePairNode; |
| import org.apache.flex.compiler.tree.as.IPackageNode; |
| import org.apache.flex.compiler.tree.as.IParameterNode; |
| import org.apache.flex.compiler.tree.as.IReturnNode; |
| import org.apache.flex.compiler.tree.as.ISwitchNode; |
| import org.apache.flex.compiler.tree.as.ITernaryOperatorNode; |
| import org.apache.flex.compiler.tree.as.IThrowNode; |
| import org.apache.flex.compiler.tree.as.ITryNode; |
| import org.apache.flex.compiler.tree.as.ITypeNode; |
| import org.apache.flex.compiler.tree.as.ITypedExpressionNode; |
| import org.apache.flex.compiler.tree.as.IUnaryOperatorNode; |
| import org.apache.flex.compiler.tree.as.IWhileLoopNode; |
| import org.apache.flex.compiler.tree.as.IWithNode; |
| |
| import com.google.debugging.sourcemap.FilePosition; |
| |
| /** |
| * @author Michael Schmalle |
| */ |
| public class JSEmitter extends ASEmitter implements IJSEmitter |
| { |
| private JSSessionModel model; |
| |
| public ISubEmitter<IContainerNode> blockOpenEmitter; |
| public ISubEmitter<IContainerNode> blockCloseEmitter; |
| public ISubEmitter<INumericLiteralNode> numericLiteralEmitter; |
| public ISubEmitter<IContainerNode> parametersEmitter; |
| public ISubEmitter<IParameterNode> parameterEmitter; |
| public ISubEmitter<IContainerNode> functionCallArgumentsEmitter; |
| public ISubEmitter<ILiteralContainerNode> literalContainerEmitter; |
| public ISubEmitter<IObjectLiteralValuePairNode> objectLiteralValuePairEmitter; |
| public ISubEmitter<IReturnNode> returnEmitter; |
| public ISubEmitter<IDynamicAccessNode> dynamicAccessEmitter; |
| public ISubEmitter<IUnaryOperatorNode> unaryOperatorEmitter; |
| public ISubEmitter<ITernaryOperatorNode> ternaryOperatorEmitter; |
| public ISubEmitter<IDefinitionNode> memberKeywordEmitter; |
| public ISubEmitter<IIfNode> ifEmitter; |
| public ISubEmitter<ISwitchNode> switchEmitter; |
| public ISubEmitter<IWhileLoopNode> whileLoopEmitter; |
| public ISubEmitter<IWhileLoopNode> doWhileLoopEmitter; |
| public ISubEmitter<IForLoopNode> forLoopEmitter; |
| public ISubEmitter<IIterationFlowNode> interationFlowEmitter; |
| public ISubEmitter<ITryNode> tryEmitter; |
| public ISubEmitter<ICatchNode> catchEmitter; |
| public ISubEmitter<IThrowNode> throwEmitter; |
| public ISubEmitter<IWithNode> withEmitter; |
| public ISubEmitter<IASNode> statementEmitter; |
| public ISubEmitter<ILanguageIdentifierNode> languageIdentifierEmitter; |
| public SourceMapDirectiveEmitter sourceMapDirectiveEmitter; |
| |
| @Override |
| public JSSessionModel getModel() |
| { |
| return model; |
| } |
| |
| private SourceMapMapping lastMapping; |
| |
| private List<SourceMapMapping> sourceMapMappings; |
| |
| public List<SourceMapMapping> getSourceMapMappings() |
| { |
| return sourceMapMappings; |
| } |
| |
| public JSEmitter(FilterWriter out) |
| { |
| super(out); |
| |
| model = new JSSessionModel(); |
| sourceMapMappings = new ArrayList<SourceMapMapping>(); |
| |
| blockOpenEmitter = new BlockOpenEmitter(this); |
| blockCloseEmitter = new BlockCloseEmitter(this); |
| numericLiteralEmitter = new NumericLiteralEmitter(this); |
| parametersEmitter = new ParametersEmitter(this); |
| parameterEmitter = new ParameterEmitter(this); |
| functionCallArgumentsEmitter = new FunctionCallArgumentsEmitter(this); |
| literalContainerEmitter = new LiteralContainerEmitter(this); |
| objectLiteralValuePairEmitter = new ObjectLiteralValuePairEmitter(this); |
| returnEmitter = new ReturnEmitter(this); |
| dynamicAccessEmitter = new DynamicAccessEmitter(this); |
| unaryOperatorEmitter = new UnaryOperatorEmitter(this); |
| ternaryOperatorEmitter = new TernaryOperatorEmitter(this); |
| memberKeywordEmitter = new MemberKeywordEmitter(this); |
| ifEmitter = new IfEmitter(this); |
| switchEmitter = new SwitchEmitter(this); |
| whileLoopEmitter = new WhileLoopEmitter(this); |
| doWhileLoopEmitter = new DoWhileLoopEmitter(this); |
| forLoopEmitter = new ForLoopEmitter(this); |
| interationFlowEmitter = new IterationFlowEmitter(this); |
| tryEmitter = new TryEmitter(this); |
| catchEmitter = new CatchEmitter(this); |
| throwEmitter = new ThrowEmitter(this); |
| withEmitter = new WithEmitter(this); |
| statementEmitter = new StatementEmitter(this); |
| languageIdentifierEmitter = new LanguageIdentifierEmitter(this); |
| sourceMapDirectiveEmitter = new SourceMapDirectiveEmitter(this); |
| } |
| |
| @Override |
| public String formatQualifiedName(String name) |
| { |
| return name; |
| } |
| |
| @Override |
| public void emitLocalNamedFunction(IFunctionNode node) |
| { |
| startMapping(node); |
| FunctionNode fnode = (FunctionNode)node; |
| write(ASEmitterTokens.FUNCTION); |
| write(ASEmitterTokens.SPACE); |
| write(fnode.getName()); |
| endMapping(node); |
| emitParameters(fnode.getParametersContainerNode()); |
| emitFunctionScope(fnode.getScopedNode()); |
| } |
| |
| @Override |
| public void emitFunctionObject(IFunctionObjectNode node) |
| { |
| startMapping(node); |
| FunctionNode fnode = node.getFunctionNode(); |
| write(ASEmitterTokens.FUNCTION); |
| endMapping(node); |
| emitParameters(fnode.getParametersContainerNode()); |
| emitFunctionScope(fnode.getScopedNode()); |
| } |
| |
| public void emitClosureStart() |
| { |
| |
| } |
| |
| public void emitClosureEnd(IASNode node, IDefinition nodeDef) |
| { |
| |
| } |
| |
| public void emitSourceMapDirective(ITypeNode node) |
| { |
| sourceMapDirectiveEmitter.emit(node); |
| } |
| |
| public void emitParameters(IContainerNode node) |
| { |
| parametersEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitParameter(IParameterNode node) |
| { |
| parameterEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitArguments(IContainerNode node) |
| { |
| functionCallArgumentsEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitNumericLiteral(INumericLiteralNode node) |
| { |
| numericLiteralEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitLiteralContainer(ILiteralContainerNode node) |
| { |
| literalContainerEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitObjectLiteralValuePair(IObjectLiteralValuePairNode node) |
| { |
| objectLiteralValuePairEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitTry(ITryNode node) |
| { |
| tryEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitCatch(ICatchNode node) |
| { |
| catchEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitWith(IWithNode node) |
| { |
| withEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitThrow(IThrowNode node) |
| { |
| throwEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitReturn(IReturnNode node) |
| { |
| returnEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitTypedExpression(ITypedExpressionNode node) |
| { |
| write(JSEmitterTokens.ARRAY); |
| } |
| |
| @Override |
| public void emitDynamicAccess(IDynamicAccessNode node) |
| { |
| dynamicAccessEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitMemberKeyword(IDefinitionNode node) |
| { |
| memberKeywordEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitUnaryOperator(IUnaryOperatorNode node) |
| { |
| unaryOperatorEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitTernaryOperator(ITernaryOperatorNode node) |
| { |
| ternaryOperatorEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitLanguageIdentifier(ILanguageIdentifierNode node) |
| { |
| languageIdentifierEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitStatement(IASNode node) |
| { |
| statementEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitIf(IIfNode node) |
| { |
| ifEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitSwitch(ISwitchNode node) |
| { |
| switchEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitImport(IImportNode node) |
| { |
| // do nothing |
| } |
| |
| @Override |
| public void emitWhileLoop(IWhileLoopNode node) |
| { |
| whileLoopEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitDoLoop(IWhileLoopNode node) |
| { |
| doWhileLoopEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitForLoop(IForLoopNode node) |
| { |
| forLoopEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitIterationFlow(IIterationFlowNode node) |
| { |
| interationFlowEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitBlockOpen(IContainerNode node) |
| { |
| blockOpenEmitter.emit(node); |
| } |
| |
| @Override |
| public void emitBlockClose(IContainerNode node) |
| { |
| blockCloseEmitter.emit(node); |
| } |
| |
| public void startMapping(ISourceLocation node) |
| { |
| startMapping(node, node.getLine(), node.getColumn()); |
| } |
| |
| public void startMapping(ISourceLocation node, int line, int column) |
| { |
| if (isBufferWrite()) |
| { |
| return; |
| } |
| IEmitter parentEmitter = getParentEmitter(); |
| if (parentEmitter != null && parentEmitter instanceof IMappingEmitter) |
| { |
| IMappingEmitter mappingParent = (IMappingEmitter) parentEmitter; |
| mappingParent.startMapping(node, line, column); |
| return; |
| } |
| if (lastMapping != null) |
| { |
| FilePosition sourceStartPosition = lastMapping.sourceStartPosition; |
| throw new IllegalStateException("Cannot start new mapping when another mapping is already started. " |
| + "Previous mapping at Line " + sourceStartPosition.getLine() |
| + " and Column " + sourceStartPosition.getColumn() |
| + " in file " + lastMapping.sourcePath); |
| } |
| |
| String sourcePath = node.getSourcePath(); |
| if (sourcePath == null) |
| { |
| //if the source path is null, this node may have been generated by |
| //the compiler automatically. for example, an untyped variable will |
| //have a node for the * type. |
| if (node instanceof IASNode) |
| { |
| IASNode parentNode = ((IASNode) node).getParent(); |
| if (parentNode != null) |
| { |
| //try the parent node |
| startMapping(parentNode, line, column); |
| return; |
| } |
| } |
| } |
| |
| SourceMapMapping mapping = new SourceMapMapping(); |
| mapping.sourcePath = sourcePath; |
| mapping.sourceStartPosition = new FilePosition(line, column); |
| mapping.destStartPosition = new FilePosition(getCurrentLine(), getCurrentColumn()); |
| lastMapping = mapping; |
| } |
| |
| public void startMapping(ISourceLocation node, ISourceLocation afterNode) |
| { |
| startMapping(node, afterNode.getEndLine(), afterNode.getEndColumn()); |
| } |
| |
| public void endMapping(ISourceLocation node) |
| { |
| if (isBufferWrite()) |
| { |
| return; |
| } |
| IEmitter parentEmitter = getParentEmitter(); |
| if (parentEmitter != null && parentEmitter instanceof IMappingEmitter) |
| { |
| IMappingEmitter mappingParent = (IMappingEmitter) parentEmitter; |
| mappingParent.endMapping(node); |
| return; |
| } |
| if (lastMapping == null) |
| { |
| throw new IllegalStateException("Cannot end mapping when a mapping has not been started"); |
| } |
| |
| lastMapping.destEndPosition = new FilePosition(getCurrentLine(), getCurrentColumn()); |
| sourceMapMappings.add(lastMapping); |
| lastMapping = null; |
| } |
| |
| /** |
| * Adjusts the line numbers saved in the source map when a line should be |
| * added during post processing. |
| * |
| * @param lineIndex |
| */ |
| protected void addLineToMappings(int lineIndex) |
| { |
| for (SourceMapMapping mapping : sourceMapMappings) |
| { |
| FilePosition destStartPosition = mapping.destStartPosition; |
| int startLine = destStartPosition.getLine(); |
| if(startLine > lineIndex) |
| { |
| mapping.destStartPosition = new FilePosition(startLine + 1, destStartPosition.getColumn()); |
| FilePosition destEndPosition = mapping.destEndPosition; |
| mapping.destEndPosition = new FilePosition(destEndPosition.getLine() + 1, destEndPosition.getColumn()); |
| } |
| } |
| } |
| |
| /** |
| * Adjusts the line numbers saved in the source map when a line should be |
| * removed during post processing. |
| * |
| * @param lineIndex |
| */ |
| protected void removeLineFromMappings(int lineIndex) |
| { |
| for (SourceMapMapping mapping : sourceMapMappings) |
| { |
| FilePosition destStartPosition = mapping.destStartPosition; |
| int startLine = destStartPosition.getLine(); |
| if(startLine > lineIndex) |
| { |
| mapping.destStartPosition = new FilePosition(startLine - 1, destStartPosition.getColumn()); |
| FilePosition destEndPosition = mapping.destEndPosition; |
| mapping.destEndPosition = new FilePosition(destEndPosition.getLine() - 1, destEndPosition.getColumn()); |
| } |
| } |
| } |
| |
| } |