blob: ae8768a8eaa3e1fb62ea5d259bf396c28825ca36 [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.codegen.js;
import java.io.FilterWriter;
import java.util.ArrayList;
import java.util.List;
import org.apache.royale.compiler.codegen.IEmitter;
import org.apache.royale.compiler.codegen.ISubEmitter;
import org.apache.royale.compiler.codegen.js.IJSEmitter;
import org.apache.royale.compiler.codegen.js.IMappingEmitter;
import org.apache.royale.compiler.common.ISourceLocation;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.internal.codegen.as.ASEmitter;
import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
import org.apache.royale.compiler.internal.codegen.js.jx.BlockCloseEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.BlockOpenEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.CatchEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.DoWhileLoopEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.DynamicAccessEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ForLoopEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.FunctionCallArgumentsEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.IfEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.IterationFlowEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.LanguageIdentifierEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.LiteralContainerEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.MemberKeywordEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.NumericLiteralEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ObjectLiteralValuePairEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ParameterEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ParametersEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ReturnEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.SourceMapDirectiveEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.StatementEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.SwitchEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.TernaryOperatorEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ThrowEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.TryEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.UnaryOperatorEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.WhileLoopEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.WithEmitter;
import org.apache.royale.compiler.internal.tree.as.FunctionNode;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.ICatchNode;
import org.apache.royale.compiler.tree.as.IContainerNode;
import org.apache.royale.compiler.tree.as.IDefinitionNode;
import org.apache.royale.compiler.tree.as.IDynamicAccessNode;
import org.apache.royale.compiler.tree.as.IForLoopNode;
import org.apache.royale.compiler.tree.as.IFunctionNode;
import org.apache.royale.compiler.tree.as.IFunctionObjectNode;
import org.apache.royale.compiler.tree.as.IIfNode;
import org.apache.royale.compiler.tree.as.IImportNode;
import org.apache.royale.compiler.tree.as.IIterationFlowNode;
import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode;
import org.apache.royale.compiler.tree.as.ILiteralContainerNode;
import org.apache.royale.compiler.tree.as.INumericLiteralNode;
import org.apache.royale.compiler.tree.as.IObjectLiteralValuePairNode;
import org.apache.royale.compiler.tree.as.IParameterNode;
import org.apache.royale.compiler.tree.as.IReturnNode;
import org.apache.royale.compiler.tree.as.ISwitchNode;
import org.apache.royale.compiler.tree.as.ITernaryOperatorNode;
import org.apache.royale.compiler.tree.as.IThrowNode;
import org.apache.royale.compiler.tree.as.ITryNode;
import org.apache.royale.compiler.tree.as.ITypeNode;
import org.apache.royale.compiler.tree.as.ITypedExpressionNode;
import org.apache.royale.compiler.tree.as.IUnaryOperatorNode;
import org.apache.royale.compiler.tree.as.IWhileLoopNode;
import org.apache.royale.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());
}
}
}
@Override
public String formatPrivateName(String className, String name) {
// TODO Auto-generated method stub
return className.replace(".", "_") + "_" + name;
}
}