Merge branches 'develop' and 'feature/maven-migration-test' of https://git-wip-us.apache.org/repos/asf/flex-falcon into feature/maven-migration-test
Project: http://git-wip-us.apache.org/repos/asf/flex-falcon/repo Commit: http://git-wip-us.apache.org/repos/asf/flex-falcon/commit/beaf5b63 Tree: http://git-wip-us.apache.org/repos/asf/flex-falcon/tree/beaf5b63 Diff: http://git-wip-us.apache.org/repos/asf/flex-falcon/diff/beaf5b63 Branch: refs/heads/feature/maven-migration-test Commit: beaf5b63a28777f160c801babaef7ce44cf87336 Parents: 316710d e9fd628 Author: Christofer Dutz <[email protected]> Authored: Mon Apr 18 15:31:26 2016 +0200 Committer: Christofer Dutz <[email protected]> Committed: Mon Apr 18 15:31:26 2016 +0200 ---------------------------------------------------------------------- .../flex/compiler/codegen/as/IASEmitter.java | 6 +- .../flex/compiler/codegen/js/IJSEmitter.java | 2 +- .../codegen/as/ASAfterNodeStrategy.java | 4 +- .../codegen/as/ASBeforeNodeStrategy.java | 4 +- .../compiler/internal/codegen/as/ASEmitter.java | 12 ++ .../compiler/internal/codegen/js/JSEmitter.java | 28 ++- .../internal/codegen/js/JSSubEmitter.java | 4 +- .../codegen/js/jx/LiteralContainerEmitter.java | 2 +- .../js/jx/ObjectLiteralValuePairEmitter.java | 6 +- .../codegen/js/jx/VarDeclarationEmitter.java | 12 +- .../js/sourcemaps/TestSourceMapExpressions.java | 181 +++++++++++-------- .../js/sourcemaps/TestSourceMapStatements.java | 144 ++++++++++++++- .../codegen/js/jx/BlockCloseEmitter.java | 43 +++++ .../codegen/js/jx/BlockOpenEmitter.java | 43 +++++ .../compiler/internal/parsing/as/ASParser.g | 4 +- .../flex/compiler/common/ISourceLocation.java | 10 + .../flex/compiler/common/SourceLocation.java | 54 ++++++ .../internal/definitions/metadata/MetaTag.java | 12 ++ .../compiler/internal/parsing/TokenBase.java | 44 +++++ .../compiler/internal/tree/as/NodeBase.java | 17 +- .../internal/tree/as/OperatorNodeBase.java | 2 + .../flex/compiler/problems/CompilerProblem.java | 12 ++ 22 files changed, 547 insertions(+), 99 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler-jx/src/main/java/org/apache/flex/compiler/codegen/as/IASEmitter.java ---------------------------------------------------------------------- diff --cc compiler-jx/src/main/java/org/apache/flex/compiler/codegen/as/IASEmitter.java index bd0d1eb,0000000..2fe9eea mode 100644,000000..100644 --- a/compiler-jx/src/main/java/org/apache/flex/compiler/codegen/as/IASEmitter.java +++ b/compiler-jx/src/main/java/org/apache/flex/compiler/codegen/as/IASEmitter.java @@@ -1,369 -1,0 +1,373 @@@ +/* + * + * 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.codegen.as; + +import java.io.Writer; + +import org.apache.flex.compiler.codegen.IDocEmitter; +import org.apache.flex.compiler.codegen.IEmitter; +import org.apache.flex.compiler.definitions.IPackageDefinition; +import org.apache.flex.compiler.internal.tree.as.LabeledStatementNode; +import org.apache.flex.compiler.tree.as.IASNode; +import org.apache.flex.compiler.tree.as.IBinaryOperatorNode; +import org.apache.flex.compiler.tree.as.IBlockNode; +import org.apache.flex.compiler.tree.as.ICatchNode; +import org.apache.flex.compiler.tree.as.IClassNode; +import org.apache.flex.compiler.tree.as.IContainerNode; +import org.apache.flex.compiler.tree.as.IDynamicAccessNode; +import org.apache.flex.compiler.tree.as.IForLoopNode; +import org.apache.flex.compiler.tree.as.IFunctionCallNode; +import org.apache.flex.compiler.tree.as.IFunctionNode; +import org.apache.flex.compiler.tree.as.IFunctionObjectNode; +import org.apache.flex.compiler.tree.as.IGetterNode; +import org.apache.flex.compiler.tree.as.IIdentifierNode; +import org.apache.flex.compiler.tree.as.IIfNode; +import org.apache.flex.compiler.tree.as.IImportNode; +import org.apache.flex.compiler.tree.as.IInterfaceNode; +import org.apache.flex.compiler.tree.as.IIterationFlowNode; +import org.apache.flex.compiler.tree.as.IKeywordNode; +import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode; +import org.apache.flex.compiler.tree.as.ILiteralContainerNode; +import org.apache.flex.compiler.tree.as.ILiteralNode; +import org.apache.flex.compiler.tree.as.IMemberAccessExpressionNode; +import org.apache.flex.compiler.tree.as.INamespaceAccessExpressionNode; +import org.apache.flex.compiler.tree.as.INamespaceNode; +import org.apache.flex.compiler.tree.as.INumericLiteralNode; +import org.apache.flex.compiler.tree.as.IObjectLiteralValuePairNode; +import org.apache.flex.compiler.tree.as.IParameterNode; +import org.apache.flex.compiler.tree.as.IReturnNode; +import org.apache.flex.compiler.tree.as.ISetterNode; +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.ITypedExpressionNode; +import org.apache.flex.compiler.tree.as.IUnaryOperatorNode; +import org.apache.flex.compiler.tree.as.IUseNamespaceNode; +import org.apache.flex.compiler.tree.as.IVariableExpressionNode; +import org.apache.flex.compiler.tree.as.IVariableNode; +import org.apache.flex.compiler.tree.as.IWhileLoopNode; +import org.apache.flex.compiler.tree.as.IWithNode; +import org.apache.flex.compiler.tree.metadata.IMetaTagNode; +import org.apache.flex.compiler.visitor.IASNodeStrategy; +import org.apache.flex.compiler.visitor.IBlockWalker; + +/** + * The {@link IASEmitter} interface allows abstraction between the + * {@link IASNodeStrategy} and the current output buffer {@link Writer}. + * + * @author Michael Schmalle + */ +public interface IASEmitter extends IEmitter +{ + IBlockWalker getWalker(); + + void setWalker(IBlockWalker asBlockWalker); + + IDocEmitter getDocEmitter(); + + void setDocEmitter(IDocEmitter value); + + String postProcess(String output); - ++ + void emitImport(IImportNode node); + + void emitPackageHeader(IPackageDefinition definition); + + void emitPackageHeaderContents(IPackageDefinition definition); + + void emitPackageContents(IPackageDefinition definition); + + void emitPackageFooter(IPackageDefinition definition); + + /** + * Emit a Class. + * + * @param node The {@link IClassNode} class. + */ + void emitClass(IClassNode node); + + /** + * Emit an Interface. + * + * @param node The {@link IInterfaceNode} class. + */ + void emitInterface(IInterfaceNode node); + + /** + * Emit a documentation comment for a Class field or constant + * {@link IVariableNode}. + * + * @param node The {@link IVariableNode} class field member. + */ + void emitFieldDocumentation(IVariableNode node); + + /** + * Emit a full Class field member. + * + * @param node The {@link IVariableNode} class field member. + */ + void emitField(IVariableNode node); + + /** + * Emit a documentation comment for a Class method {@link IFunctionNode}. + * + * @param node The {@link IFunctionNode} class method member. + */ + void emitMethodDocumentation(IFunctionNode node); + + /** + * Emit a full Class or Interface method member. + * + * @param node The {@link IFunctionNode} class method member. + */ + void emitMethod(IFunctionNode node); + + /** + * Emit a documentation comment for a Class method {@link IGetterNode}. + * + * @param node The {@link IGetterNode} class accessor member. + */ + void emitGetAccessorDocumentation(IGetterNode node); + + /** + * Emit a full Class getter member. + * + * @param node The {@link IVariableNode} class getter member. + */ + void emitGetAccessor(IGetterNode node); + + /** + * Emit a documentation comment for a Class accessor {@link IGetterNode}. + * + * @param node The {@link ISetterNode} class accessor member. + */ + void emitSetAccessorDocumentation(ISetterNode node); + + /** + * Emit a full Class setter member. + * + * @param node The {@link ISetterNode} class setter member. + */ + void emitSetAccessor(ISetterNode node); + + void emitParameter(IParameterNode node); + + /** + * Emit a namespace member. + * + * @param node The {@link INamespaceNode} class member. + */ + void emitNamespace(INamespaceNode node); + + //-------------------------------------------------------------------------- + // Statements + //-------------------------------------------------------------------------- + + /** + * Emit a statement found within an {@link IBlockNode}. + * + * @param node The {@link IASNode} statement. + */ + void emitStatement(IASNode node); + + /** + * Emit a <code>if(){}else if(){}else{}</code> statement. + * + * @param node The {@link IIfNode} node. + */ + void emitIf(IIfNode node); + + /** + * Emit a <code>for each</code> statement. + * + * @param node The {@link IForLoopNode} node. + */ + void emitForEachLoop(IForLoopNode node); + + /** + * Emit a <code>for</code> statement. + * + * @param node The {@link IForLoopNode} node. + */ + void emitForLoop(IForLoopNode node); + + /** + * Emit a <code>switch(){}</code> statement. + * + * @param node The {@link ISwitchNode} node. + */ + void emitSwitch(ISwitchNode node); + + /** + * Emit a <code>while(){}</code> statement. + * + * @param node The {@link IWhileLoopNode} node. + */ + void emitWhileLoop(IWhileLoopNode node); + + /** + * Emit a <code>do{}while()</code> statement. + * + * @param node The {@link IWhileLoopNode} node. + */ + void emitDoLoop(IWhileLoopNode node); + + /** + * Emit a <code>with(){}</code> statement. + * + * @param node The {@link IWithNode} node. + */ + void emitWith(IWithNode node); + + /** + * Emit a <code>throw</code> statement. + * + * @param node The {@link IThrowNode} node. + */ + void emitThrow(IThrowNode node); + + /** + * Emit a <code>try{}</code> statement. + * + * @param node The {@link ITryNode} node. + */ + void emitTry(ITryNode node); + + /** + * Emit a <code>catch(){}</code> statement. + * + * @param node The {@link ICatchNode} node. + */ + void emitCatch(ICatchNode node); + + /** + * Emit a <code>foo:{}</code> statement. + * + * @param node The {@link LabeledStatementNode} node. + */ + void emitLabelStatement(LabeledStatementNode node); + + void emitReturn(IReturnNode node); + + //-------------------------------------------------------------------------- + // Expressions + //-------------------------------------------------------------------------- + + /** + * Emit a variable declaration found in expression statements within scoped + * blocks. + * + * @param node The {@link IVariableNode} or chain of variable nodes. + */ + void emitVarDeclaration(IVariableNode node); + + /** + * Emit an anonymous {@link IFunctionObjectNode}. + * + * @param node The anonymous {@link IFunctionObjectNode}. + */ + void emitFunctionObject(IFunctionObjectNode node); + + /** + * Emit an local named function {@link IFunctionNode}. + * + * @param node The local named function {@link IFunctionNode}. + */ + void emitLocalNamedFunction(IFunctionNode node); + + /** + * Emit a header at the start of a function block. + * + * @param node The {@link IFunctionNode} node. + */ + void emitFunctionBlockHeader(IFunctionNode node); + + /** + * Emit a function call like <code>new Foo()</code> or <code>foo(42)</code>. + * + * @param node The {@link IFunctionCallNode} node. + */ + void emitFunctionCall(IFunctionCallNode node); + + void emitArguments(IContainerNode node); + + void emitIterationFlow(IIterationFlowNode node); + + void emitNamespaceAccessExpression(INamespaceAccessExpressionNode node); + + void emitMemberAccessExpression(IMemberAccessExpressionNode node); + + void emitVariableExpression(IVariableExpressionNode node); + + void emitDynamicAccess(IDynamicAccessNode node); + + void emitTypedExpression(ITypedExpressionNode node); + + void emitObjectLiteralValuePair(IObjectLiteralValuePairNode node); + + void emitIdentifier(IIdentifierNode node); + + void emitLiteral(ILiteralNode node); + + void emitLiteralContainer(ILiteralContainerNode node); + + void emitNumericLiteral(INumericLiteralNode node); + + //-------------------------------------------------------------------------- + // Operators + //-------------------------------------------------------------------------- + + void emitUnaryOperator(IUnaryOperatorNode node); + + void emitAsOperator(IBinaryOperatorNode node); + + void emitIsOperator(IBinaryOperatorNode node); + + /** + * Emit an operator statement. + * + * @param node The {@link IBinaryOperatorNode} or chain of variable nodes. + */ + void emitBinaryOperator(IBinaryOperatorNode node); + + void emitTernaryOperator(ITernaryOperatorNode node); + + //-------------------------------------------------------------------------- + // Node + //-------------------------------------------------------------------------- + + void emitKeyword(IKeywordNode node); + + void emitLanguageIdentifier(ILanguageIdentifierNode node); + + void emitMetaTag(IMetaTagNode node); + + void emitContainer(IContainerNode node); + + void emitE4XFilter(IMemberAccessExpressionNode node); + + void emitUseNamespace(IUseNamespaceNode node); + ++ void emitBlockOpen(IContainerNode node); ++ ++ void emitBlockClose(IContainerNode node); ++ +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler-jx/src/main/java/org/apache/flex/compiler/codegen/js/IJSEmitter.java ---------------------------------------------------------------------- diff --cc compiler-jx/src/main/java/org/apache/flex/compiler/codegen/js/IJSEmitter.java index 218b5be,0000000..287b8c8 mode 100644,000000..100644 --- a/compiler-jx/src/main/java/org/apache/flex/compiler/codegen/js/IJSEmitter.java +++ b/compiler-jx/src/main/java/org/apache/flex/compiler/codegen/js/IJSEmitter.java @@@ -1,85 -1,0 +1,85 @@@ +/* + * + * 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.codegen.js; + +import java.io.Writer; +import java.util.List; + +import com.google.debugging.sourcemap.FilePosition; +import org.apache.flex.compiler.codegen.as.IASEmitter; +import org.apache.flex.compiler.common.ISourceLocation; +import org.apache.flex.compiler.internal.codegen.js.JSSessionModel; +import org.apache.flex.compiler.tree.as.IASNode; +import org.apache.flex.compiler.tree.as.ITypeNode; +import org.apache.flex.compiler.visitor.IASNodeStrategy; + +/** + * The {@link IJSEmitter} interface allows abstraction between the + * {@link IASNodeStrategy} and the current output buffer {@link Writer}. + * + * @author Michael Schmalle + */ +public interface IJSEmitter extends IASEmitter +{ + JSSessionModel getModel(); + List<SourceMapMapping> getSourceMapMappings(); + + String formatQualifiedName(String name); + + /** + * Adds a node to the source map. + */ + void startMapping(ISourceLocation node); + + /** + * Adds a node to the source map using custom line and column values, + * instead of the node's own line and column. Useful for starting a mapping + * in the middle of the node. + */ + void startMapping(ISourceLocation node, int line, int column); + + /** + * Adds a node to the source map after a particular node instead using the + * node's own line and column. + */ - void startMapping(ISourceLocation node, ISourceLocation nodeBeforeMapping); ++ void startMapping(ISourceLocation node, ISourceLocation afterNode); + + /** + * Commits a mapping to the source map. + */ + void endMapping(ISourceLocation node); + + void pushSourceMapName(ISourceLocation node); + void popSourceMapName(); + + void emitSourceMapDirective(ITypeNode node); + + void emitClosureStart(); + void emitClosureEnd(IASNode node); + + class SourceMapMapping + { + public String sourcePath; + public String name; + public FilePosition sourceStartPosition; + public FilePosition destStartPosition; + public FilePosition destEndPosition; + } +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASAfterNodeStrategy.java ---------------------------------------------------------------------- diff --cc compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASAfterNodeStrategy.java index 1ac85f7,0000000..b274b57 mode 100644,000000..100644 --- a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASAfterNodeStrategy.java +++ b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASAfterNodeStrategy.java @@@ -1,77 -1,0 +1,77 @@@ +/* + * + * 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.as; + +import org.apache.flex.compiler.codegen.IEmitter; +import org.apache.flex.compiler.codegen.as.IASEmitter; +import org.apache.flex.compiler.codegen.js.IJSEmitter; +import org.apache.flex.compiler.tree.ASTNodeID; +import org.apache.flex.compiler.tree.as.IASNode; +import org.apache.flex.compiler.tree.as.IContainerNode; +import org.apache.flex.compiler.tree.as.IContainerNode.ContainerType; +import org.apache.flex.compiler.visitor.IASNodeStrategy; + +/** + * A concrete implementation of the {@link IASNodeStrategy} that allows + * {@link IASNode} processing after the current node handler. + * <p> + * The class has access to the current {@link IJSEmitter} instance being used to + * output source code to the current output buffer. + * + * @author Michael Schmalle + */ +public class ASAfterNodeStrategy implements IASNodeStrategy +{ + private final IASEmitter emitter; + + public ASAfterNodeStrategy(IASEmitter emitter) + { + this.emitter = emitter; + } + + @Override + public void handle(IASNode node) + { + if (node.getNodeID() == ASTNodeID.BlockID) + { + IContainerNode container = (IContainerNode) node; + ContainerType type = container.getContainerType(); + if (type != ContainerType.IMPLICIT + && type != ContainerType.SYNTHESIZED) + { + if (node.getChildCount() != 0) + { + emitter.indentPop(); - ((IEmitter) emitter).writeNewline(); ++ emitter.writeNewline(); + } + - ((IEmitter) emitter).write(ASEmitterTokens.BLOCK_CLOSE); ++ emitter.emitBlockClose(container); + } + else if (type == ContainerType.IMPLICIT + || type == ContainerType.SYNTHESIZED) + { + if (node.getChildCount() != 0) + { + emitter.indentPop(); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASBeforeNodeStrategy.java ---------------------------------------------------------------------- diff --cc compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASBeforeNodeStrategy.java index d481573,0000000..d960057 mode 100644,000000..100644 --- a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASBeforeNodeStrategy.java +++ b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASBeforeNodeStrategy.java @@@ -1,74 -1,0 +1,74 @@@ +/* + * + * 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.as; + +import org.apache.flex.compiler.codegen.IEmitter; +import org.apache.flex.compiler.codegen.as.IASEmitter; +import org.apache.flex.compiler.tree.ASTNodeID; +import org.apache.flex.compiler.tree.as.IASNode; +import org.apache.flex.compiler.tree.as.IContainerNode; +import org.apache.flex.compiler.tree.as.IContainerNode.ContainerType; +import org.apache.flex.compiler.visitor.IASNodeStrategy; + +/** + * A concrete implementation of the {@link IASNodeStrategy} that allows + * {@link IASNode} processing before the current node handler. + * + * @author Michael Schmalle + */ +public class ASBeforeNodeStrategy implements IASNodeStrategy +{ + private final IASEmitter emitter; + + public ASBeforeNodeStrategy(IASEmitter emitter) + { + this.emitter = emitter; + } + + @Override + public void handle(IASNode node) + { + if (node.getNodeID() == ASTNodeID.BlockID) + { + IASNode parent = node.getParent(); + IContainerNode container = (IContainerNode) node; + ContainerType type = container.getContainerType(); + + if (parent.getNodeID() != ASTNodeID.LabledStatementID) + { + if (node.getChildCount() != 0) + emitter.indentPush(); + } + + // switch cases are SYNTHESIZED + if (type != ContainerType.IMPLICIT + && type != ContainerType.SYNTHESIZED) + { - ((IEmitter) emitter).write(ASEmitterTokens.BLOCK_OPEN); ++ emitter.emitBlockOpen(container); + } + + if (parent.getNodeID() != ASTNodeID.LabledStatementID) + { - ((IEmitter) emitter).writeNewline(); ++ emitter.writeNewline(); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASEmitter.java ---------------------------------------------------------------------- diff --cc compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASEmitter.java index 875d1d2,0000000..6f48f8e mode 100644,000000..100644 --- a/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASEmitter.java +++ b/compiler-jx/src/main/java/org/apache/flex/compiler/internal/codegen/as/ASEmitter.java @@@ -1,1540 -1,0 +1,1552 @@@ +/* + * + * 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.as; + +import java.io.FilterWriter; +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import org.apache.flex.compiler.codegen.IDocEmitter; +import org.apache.flex.compiler.codegen.IEmitter; +import org.apache.flex.compiler.codegen.IEmitterTokens; +import org.apache.flex.compiler.codegen.as.IASEmitter; +import org.apache.flex.compiler.common.ASModifier; +import org.apache.flex.compiler.common.IImportTarget; +import org.apache.flex.compiler.common.ModifiersSet; +import org.apache.flex.compiler.constants.IASKeywordConstants; +import org.apache.flex.compiler.definitions.IDefinition; +import org.apache.flex.compiler.definitions.IFunctionDefinition; +import org.apache.flex.compiler.definitions.IPackageDefinition; +import org.apache.flex.compiler.definitions.ITypeDefinition; +import org.apache.flex.compiler.definitions.IVariableDefinition; +import org.apache.flex.compiler.internal.codegen.js.utils.EmitterUtils; +import org.apache.flex.compiler.internal.tree.as.ChainedVariableNode; +import org.apache.flex.compiler.internal.tree.as.ContainerNode; +import org.apache.flex.compiler.internal.tree.as.FunctionNode; +import org.apache.flex.compiler.internal.tree.as.LabeledStatementNode; +import org.apache.flex.compiler.problems.ICompilerProblem; +import org.apache.flex.compiler.tree.ASTNodeID; +import org.apache.flex.compiler.tree.as.IASNode; +import org.apache.flex.compiler.tree.as.IAccessorNode; +import org.apache.flex.compiler.tree.as.IBinaryOperatorNode; +import org.apache.flex.compiler.tree.as.ICatchNode; +import org.apache.flex.compiler.tree.as.IClassNode; +import org.apache.flex.compiler.tree.as.IConditionalNode; +import org.apache.flex.compiler.tree.as.IContainerNode; +import org.apache.flex.compiler.tree.as.IContainerNode.ContainerType; +import org.apache.flex.compiler.tree.as.IDefinitionNode; +import org.apache.flex.compiler.tree.as.IDynamicAccessNode; +import org.apache.flex.compiler.tree.as.IExpressionNode; +import org.apache.flex.compiler.tree.as.IForLoopNode; +import org.apache.flex.compiler.tree.as.IFunctionCallNode; +import org.apache.flex.compiler.tree.as.IFunctionNode; +import org.apache.flex.compiler.tree.as.IFunctionObjectNode; +import org.apache.flex.compiler.tree.as.IGetterNode; +import org.apache.flex.compiler.tree.as.IIdentifierNode; +import org.apache.flex.compiler.tree.as.IIfNode; +import org.apache.flex.compiler.tree.as.IImportNode; +import org.apache.flex.compiler.tree.as.IInterfaceNode; +import org.apache.flex.compiler.tree.as.IIterationFlowNode; +import org.apache.flex.compiler.tree.as.IKeywordNode; +import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode; +import org.apache.flex.compiler.tree.as.ILiteralContainerNode; +import org.apache.flex.compiler.tree.as.ILiteralNode; +import org.apache.flex.compiler.tree.as.IMemberAccessExpressionNode; +import org.apache.flex.compiler.tree.as.INamespaceAccessExpressionNode; +import org.apache.flex.compiler.tree.as.INamespaceNode; +import org.apache.flex.compiler.tree.as.INumericLiteralNode; +import org.apache.flex.compiler.tree.as.IObjectLiteralValuePairNode; +import org.apache.flex.compiler.tree.as.IOperatorNode; +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.IScopedNode; +import org.apache.flex.compiler.tree.as.ISetterNode; +import org.apache.flex.compiler.tree.as.IStatementNode; +import org.apache.flex.compiler.tree.as.ISwitchNode; +import org.apache.flex.compiler.tree.as.ITerminalNode; +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.IUseNamespaceNode; +import org.apache.flex.compiler.tree.as.IVariableExpressionNode; +import org.apache.flex.compiler.tree.as.IVariableNode; +import org.apache.flex.compiler.tree.as.IWhileLoopNode; +import org.apache.flex.compiler.tree.as.IWithNode; +import org.apache.flex.compiler.tree.metadata.IMetaTagNode; +import org.apache.flex.compiler.utils.ASNodeUtils; +import org.apache.flex.compiler.visitor.IBlockWalker; +import org.apache.flex.compiler.visitor.as.IASBlockWalker; + +/** + * The base implementation for an ActionScript emitter. + * + * @author Michael Schmalle + */ +public class ASEmitter implements IASEmitter, IEmitter +{ + private final FilterWriter out; + + private boolean bufferWrite; + + protected boolean isBufferWrite() + { + return bufferWrite; + } + + public void setBufferWrite(boolean value) + { + bufferWrite = value; + } + + private StringBuilder builder; + + protected StringBuilder getBuilder() + { + return builder; + } + + protected void setBuilder(StringBuilder sb) + { + builder = sb; + } + + protected void flushBuilder() + { + setBufferWrite(false); + write(builder.toString()); + builder.setLength(0); + } + + // (mschmalle) think about how this should be implemented, we can add our + // own problems to this, they don't just have to be parse problems + public List<ICompilerProblem> getProblems() + { + return walker.getErrors(); + } + + private int currentIndent = 0; + + protected int getCurrentIndent() + { + return currentIndent; + } + + protected void writeIndent() + { + write(ASEmitterTokens.INDENT); + } + + private IASBlockWalker walker; + + @Override + public IBlockWalker getWalker() + { + return walker; + } + + @Override + public void setWalker(IBlockWalker value) + { + walker = (IASBlockWalker) value; + } + + @Override + public IDocEmitter getDocEmitter() + { + return null; + } + + @Override + public void setDocEmitter(IDocEmitter value) + { + } + + private int currentLine = 0; + + protected int getCurrentLine() + { + return currentLine; + } + + private int currentColumn = 0; + + protected int getCurrentColumn() + { + return currentColumn; + } + + public ASEmitter(FilterWriter out) + { + this.out = out; + builder = new StringBuilder(); + } + + @Override + public String postProcess(String output) + { + return output; + } + + @Override + public void write(IEmitterTokens value) + { + write(value.getToken()); + } + + @Override + public void write(String value) + { + try + { + int newLineCount = value.length() - value.replace("\n", "").length(); + currentLine += newLineCount; + if (newLineCount > 0) + { + currentColumn = value.length() - value.lastIndexOf("\n") - 1; + } + else + { + currentColumn += value.length(); + } + if (!bufferWrite) + out.write(value); + else + builder.append(value); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + protected String getIndent(int numIndent) + { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < numIndent; i++) + sb.append(ASEmitterTokens.INDENT.getToken()); + return sb.toString(); + } + + @Override + public void indentPush() + { + currentIndent++; + } + + @Override + public void indentPop() + { + currentIndent--; + } + + @Override + public void writeNewline() + { + write(ASEmitterTokens.NEW_LINE); + write(getIndent(currentIndent)); + } + + @Override + public void writeNewline(IEmitterTokens value) + { + writeNewline(value.getToken()); + } + + @Override + public void writeNewline(String value) + { + write(value); + writeNewline(); + } + + @Override + public void writeNewline(IEmitterTokens value, boolean pushIndent) + { + writeNewline(value.getToken(), pushIndent); + } + + @Override + public void writeNewline(String value, boolean pushIndent) + { + if (pushIndent) + indentPush(); + else + indentPop(); + write(value); + writeNewline(); + } + + public void writeSymbol(String value) + { + write(value); + } + + @Override + public void writeToken(IEmitterTokens value) + { + writeToken(value.getToken()); + } + + @Override + public void writeToken(String value) + { + write(value); + write(ASEmitterTokens.SPACE); + } + + //-------------------------------------------------------------------------- + // IPackageNode + //-------------------------------------------------------------------------- + + @Override + public void emitImport(IImportNode node) + { + IImportTarget target = node.getImportTarget(); + writeToken(ASEmitterTokens.IMPORT); + write(target.toString()); + } + + @Override + public void emitPackageHeader(IPackageDefinition definition) + { + write(ASEmitterTokens.PACKAGE); + + IPackageNode node = definition.getNode(); + String name = node.getQualifiedName(); + if (name != null && !name.equals("")) + { + write(ASEmitterTokens.SPACE); + getWalker().walk(node.getNameExpressionNode()); + } + + write(ASEmitterTokens.SPACE); + write(ASEmitterTokens.BLOCK_OPEN); + } + + @Override + public void emitPackageHeaderContents(IPackageDefinition definition) + { + } + + @Override + public void emitPackageContents(IPackageDefinition definition) + { + IPackageNode node = definition.getNode(); + ITypeNode tnode = EmitterUtils.findTypeNode(node); + if (tnode != null) + { + indentPush(); + writeNewline(); + getWalker().walk(tnode); // IClassNode | IInterfaceNode + } + } + + @Override + public void emitPackageFooter(IPackageDefinition definition) + { + indentPop(); + writeNewline(); + write(ASEmitterTokens.BLOCK_CLOSE); + } + + //-------------------------------------------------------------------------- + // + //-------------------------------------------------------------------------- + + @Override + public void emitClass(IClassNode node) + { + writeToken(node.getNamespace()); + + if (node.hasModifier(ASModifier.FINAL)) + { + writeToken(ASEmitterTokens.FINAL); + } + else if (node.hasModifier(ASModifier.DYNAMIC)) + { + writeToken(ASEmitterTokens.DYNAMIC); + } + + writeToken(ASEmitterTokens.CLASS); + getWalker().walk(node.getNameExpressionNode()); + write(ASEmitterTokens.SPACE); + + IExpressionNode bnode = node.getBaseClassExpressionNode(); + if (bnode != null) + { + writeToken(ASEmitterTokens.EXTENDS); + getWalker().walk(bnode); + write(ASEmitterTokens.SPACE); + } + + IExpressionNode[] inodes = node.getImplementedInterfaceNodes(); + final int ilen = inodes.length; + if (ilen != 0) + { + writeToken(ASEmitterTokens.IMPLEMENTS); + for (int i = 0; i < ilen; i++) + { + getWalker().walk(inodes[i]); + if (i < ilen - 1) + { + writeToken(ASEmitterTokens.COMMA); + } + } + write(ASEmitterTokens.SPACE); + } + + write(ASEmitterTokens.BLOCK_OPEN); + + // fields, methods, namespaces + final IDefinitionNode[] members = node.getAllMemberNodes(); + if (members.length > 0) + { + indentPush(); + writeNewline(); + + final int len = members.length; + int i = 0; + for (IDefinitionNode mnode : members) + { + getWalker().walk(mnode); + if (mnode.getNodeID() == ASTNodeID.VariableID) + { + write(ASEmitterTokens.SEMICOLON); + if (i < len - 1) + writeNewline(); + } + else if (mnode.getNodeID() == ASTNodeID.FunctionID) + { + if (i < len - 1) + writeNewline(); + } + else if (mnode.getNodeID() == ASTNodeID.GetterID + || mnode.getNodeID() == ASTNodeID.SetterID) + { + if (i < len - 1) + writeNewline(); + } + i++; + } + + indentPop(); + } + + writeNewline(); + write(ASEmitterTokens.BLOCK_CLOSE); + } + + @Override + public void emitInterface(IInterfaceNode node) + { + writeToken(node.getNamespace()); + + writeToken(ASEmitterTokens.INTERFACE); + getWalker().walk(node.getNameExpressionNode()); + write(ASEmitterTokens.SPACE); + + IExpressionNode[] inodes = node.getExtendedInterfaceNodes(); + final int ilen = inodes.length; + if (ilen != 0) + { + writeToken(ASEmitterTokens.EXTENDS); + for (int i = 0; i < ilen; i++) + { + getWalker().walk(inodes[i]); + if (i < ilen - 1) + { + writeToken(ASEmitterTokens.COMMA); + } + } + write(ASEmitterTokens.SPACE); + } + + write(ASEmitterTokens.BLOCK_OPEN); + + final IDefinitionNode[] members = node.getAllMemberDefinitionNodes(); + if (members.length > 0) + { + indentPush(); + writeNewline(); + + final int len = members.length; + int i = 0; + for (IDefinitionNode mnode : members) + { + getWalker().walk(mnode); + write(ASEmitterTokens.SEMICOLON); + if (i < len - 1) + writeNewline(); + i++; + } + + indentPop(); + } + + writeNewline(); + write(ASEmitterTokens.BLOCK_CLOSE); + } + + //-------------------------------------------------------------------------- + // + //-------------------------------------------------------------------------- + + @Override + public void emitVarDeclaration(IVariableNode node) + { + if (!(node instanceof ChainedVariableNode)) + { + emitMemberKeyword(node); + } + + emitDeclarationName(node); + emitType(node.getVariableTypeNode()); + + IExpressionNode avnode = node.getAssignedValueNode(); + if (avnode != null) + { + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.EQUAL); + emitAssignedValue(avnode); + } + + if (!(node instanceof ChainedVariableNode)) + { + // check for chained variables + int len = node.getChildCount(); + for (int i = 0; i < len; i++) + { + IASNode child = node.getChild(i); + if (child instanceof ChainedVariableNode) + { + writeToken(ASEmitterTokens.COMMA); + emitVarDeclaration((IVariableNode) child); + } + } + } + + // the client such as IASBlockWalker is responsible for the + // semi-colon and newline handling + } + + //-------------------------------------------------------------------------- + // + //-------------------------------------------------------------------------- + + @Override + public void emitFieldDocumentation(IVariableNode node) + { + } + + @Override + public void emitField(IVariableNode node) + { + emitFieldDocumentation(node); + + IVariableDefinition definition = (IVariableDefinition) node + .getDefinition(); + + if (!(node instanceof ChainedVariableNode)) + { + emitNamespaceIdentifier(node); + emitModifiers(definition); + emitMemberKeyword(node); + } + + emitMemberName(node); + emitType(node.getVariableTypeNode()); + + IExpressionNode avnode = node.getAssignedValueNode(); + if (avnode != null) + { + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.EQUAL); + emitAssignedValue(avnode); + } + + if (!(node instanceof ChainedVariableNode)) + { + // check for chained variables + int len = node.getChildCount(); + for (int i = 0; i < len; i++) + { + IASNode child = node.getChild(i); + if (child instanceof ChainedVariableNode) + { + writeToken(ASEmitterTokens.COMMA); + emitField((IVariableNode) child); + } + } + } + + // the client such as IASBlockWalker is responsible for the + // semi-colon and newline handling + } + + //-------------------------------------------------------------------------- + // + //-------------------------------------------------------------------------- + + @Override + public void emitMethodDocumentation(IFunctionNode node) + { + } + + @Override + public void emitMethod(IFunctionNode node) + { + // see below, this is temp, I don't want a bunch of duplicated code + // at them moment, subclasses can refine anyways, we are generalizing + if (node instanceof IGetterNode) + { + emitGetAccessorDocumentation((IGetterNode) node); + } + else if (node instanceof ISetterNode) + { + emitSetAccessorDocumentation((ISetterNode) node); + } + else + { + emitMethodDocumentation(node); + } + + FunctionNode fn = (FunctionNode) node; + fn.parseFunctionBody(getProblems()); + + IFunctionDefinition definition = node.getDefinition(); + + emitNamespaceIdentifier(node); + emitModifiers(definition); + emitMemberKeyword(node); + + // I'm cheating right here, I haven't "seen" the light + // on how to properly and efficiently deal with accessors since they are SO alike + // I don't want to lump them in with methods because implementations in the + // future need to know the difference without loopholes + if (node instanceof IAccessorNode) + { + emitAccessorKeyword(((IAccessorNode) node).getAccessorKeywordNode()); + } + + emitMemberName(node); + emitParameters(node.getParametersContainerNode()); + emitType(node.getReturnTypeNode()); + if (node.getParent().getParent().getNodeID() == ASTNodeID.ClassID) + { + emitMethodScope(node.getScopedNode()); + } + + // the client such as IASBlockWalker is responsible for the + // semi-colon and newline handling + } + + @Override + public void emitGetAccessorDocumentation(IGetterNode node) + { + } + + @Override + public void emitGetAccessor(IGetterNode node) + { + // just cheat for now, IGetterNode is a IFunctionNode + emitMethod(node); + } + + @Override + public void emitSetAccessorDocumentation(ISetterNode node) + { + } + + @Override + public void emitSetAccessor(ISetterNode node) + { + // just cheat for now, ISetterNode is a IFunctionNode + emitMethod(node); + } + + @Override + public void emitLocalNamedFunction(IFunctionNode node) + { + FunctionNode fnode = (FunctionNode) node; + write(ASEmitterTokens.FUNCTION); + write(ASEmitterTokens.SPACE); + write(fnode.getName()); + emitParameters(fnode.getParametersContainerNode()); + emitType(fnode.getTypeNode()); + emitFunctionScope(fnode.getScopedNode()); + } + + @Override + public void emitFunctionObject(IFunctionObjectNode node) + { + FunctionNode fnode = node.getFunctionNode(); + write(ASEmitterTokens.FUNCTION); + emitParameters(fnode.getParametersContainerNode()); + emitType(fnode.getTypeNode()); + emitFunctionScope(fnode.getScopedNode()); + } + + //-------------------------------------------------------------------------- + // + //-------------------------------------------------------------------------- + + @Override + public void emitNamespace(INamespaceNode node) + { + emitNamespaceIdentifier(node); + writeToken(ASEmitterTokens.NAMESPACE); + emitMemberName(node); + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.EQUAL); + getWalker().walk(node.getNamespaceURINode()); + } + + //-------------------------------------------------------------------------- + // + //-------------------------------------------------------------------------- + + protected void emitNamespaceIdentifier(IDefinitionNode node) + { + String namespace = node.getNamespace(); + if (namespace != null + && !namespace.equals(IASKeywordConstants.INTERNAL)) + { + writeToken(namespace); + } + } + + protected void emitModifiers(IDefinition definition) + { + ModifiersSet modifierSet = definition.getModifiers(); + if (modifierSet.hasModifiers()) + { + for (ASModifier modifier : modifierSet.getAllModifiers()) + { + writeToken(modifier.toString()); + } + } + } + + public void emitMemberKeyword(IDefinitionNode node) + { + if (node instanceof IFunctionNode) + { + writeToken(ASEmitterTokens.FUNCTION); + } + else if (node instanceof IVariableNode) + { + writeToken(((IVariableNode) node).isConst() ? ASEmitterTokens.CONST + : ASEmitterTokens.VAR); + } + } + + protected void emitMemberName(IDefinitionNode node) + { + getWalker().walk(node.getNameExpressionNode()); + } + + public void emitDeclarationName(IDefinitionNode node) + { + getWalker().walk(node.getNameExpressionNode()); + } + + public void emitParameters(IContainerNode node) + { + write(ASEmitterTokens.PAREN_OPEN); + int len = node.getChildCount(); + for (int i = 0; i < len; i++) + { + IParameterNode parameterNode = (IParameterNode) node.getChild(i); + getWalker().walk(parameterNode); //emitParameter + if (i < len - 1) + { + writeToken(ASEmitterTokens.COMMA); + } + } + write(ASEmitterTokens.PAREN_CLOSE); + } + + @Override + public void emitParameter(IParameterNode node) + { + if (node.isRest()) + { + write(ASEmitterTokens.ELLIPSIS); + write(node.getName()); + } + else + { + getWalker().walk(node.getNameExpressionNode()); + write(ASEmitterTokens.COLON); + getWalker().walk(node.getVariableTypeNode()); + IExpressionNode anode = node.getAssignedValueNode(); + if (anode != null) + { + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.EQUAL); + getWalker().walk(anode); + } + } + } + + protected void emitType(IExpressionNode node) + { + // TODO (mschmalle) node.getVariableTypeNode() will return "*" if undefined, what to use? + // or node.getReturnTypeNode() + if (node != null) + { + write(ASEmitterTokens.COLON); + getWalker().walk(node); + } + } + + protected void emitAssignedValue(IExpressionNode node) + { + if (node == null) + { + return; + } + getWalker().walk(node); + } + + @Override + public void emitFunctionBlockHeader(IFunctionNode node) + { + // nothing to do in AS + } + + public void emitMethodScope(IScopedNode node) + { + write(ASEmitterTokens.SPACE); + getWalker().walk(node); + } + + protected void emitAccessorKeyword(IKeywordNode node) + { + getWalker().walk(node); + write(ASEmitterTokens.SPACE); + } + + protected void emitFunctionScope(IScopedNode node) + { + emitMethodScope(node); + } + + //-------------------------------------------------------------------------- + // Statements + //-------------------------------------------------------------------------- + + @Override + public void emitStatement(IASNode node) + { + getWalker().walk(node); + // XXX (mschmalle) this should be in the after handler? + if (node.getParent().getNodeID() != ASTNodeID.LabledStatementID + && node.getNodeID() != ASTNodeID.ConfigBlockID + && !(node instanceof IStatementNode)) + { + write(ASEmitterTokens.SEMICOLON); + } + + if (!isLastStatement(node)) + writeNewline(); + } + + @Override + public void emitIf(IIfNode node) + { + IConditionalNode conditional = (IConditionalNode) node.getChild(0); + + IContainerNode xnode = (IContainerNode) conditional + .getStatementContentsNode(); + + writeToken(ASEmitterTokens.IF); + //write(SPACE); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(conditional.getChild(0)); // conditional expression + write(ASEmitterTokens.PAREN_CLOSE); + if (!isImplicit(xnode)) + write(ASEmitterTokens.SPACE); + + getWalker().walk(conditional.getChild(1)); // BlockNode + IConditionalNode[] nodes = node.getElseIfNodes(); + if (nodes.length > 0) + { + for (int i = 0; i < nodes.length; i++) + { + IConditionalNode enode = nodes[i]; + IContainerNode snode = (IContainerNode) enode + .getStatementContentsNode(); + + final boolean isImplicit = isImplicit(snode); + if (isImplicit) + writeNewline(); + else + write(ASEmitterTokens.SPACE); + + writeToken(ASEmitterTokens.ELSE); + writeToken(ASEmitterTokens.IF); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(enode.getChild(0)); + write(ASEmitterTokens.PAREN_CLOSE); + if (!isImplicit) + write(ASEmitterTokens.SPACE); + + getWalker().walk(enode.getChild(1)); // ConditionalNode + } + } + + ITerminalNode elseNode = node.getElseNode(); + if (elseNode != null) + { + IContainerNode cnode = (IContainerNode) elseNode.getChild(0); + // if an implicit if, add a newline with no space + final boolean isImplicit = isImplicit(cnode); + if (isImplicit) + writeNewline(); + else + write(ASEmitterTokens.SPACE); + write(ASEmitterTokens.ELSE); + if (!isImplicit) + write(ASEmitterTokens.SPACE); + + getWalker().walk(elseNode); // TerminalNode + } + } + + @Override + public void emitForEachLoop(IForLoopNode node) + { + IContainerNode xnode = (IContainerNode) node.getChild(1); + writeToken(ASEmitterTokens.FOR); + writeToken(ASEmitterTokens.EACH); + write(ASEmitterTokens.PAREN_OPEN); + + IContainerNode cnode = node.getConditionalsContainerNode(); + getWalker().walk(cnode.getChild(0)); + + write(ASEmitterTokens.PAREN_CLOSE); + if (!isImplicit(xnode)) + write(ASEmitterTokens.SPACE); + + getWalker().walk(node.getStatementContentsNode()); + } + + @Override + public void emitForLoop(IForLoopNode node) + { + IContainerNode xnode = (IContainerNode) node.getChild(1); + + writeToken(ASEmitterTokens.FOR); + write(ASEmitterTokens.PAREN_OPEN); + + IContainerNode cnode = node.getConditionalsContainerNode(); + final IASNode node0 = cnode.getChild(0); + if (node0.getNodeID() == ASTNodeID.Op_InID) + { + getWalker().walk(cnode.getChild(0)); + } + else + { + visitForBody(cnode); + } + + write(ASEmitterTokens.PAREN_CLOSE); + if (!isImplicit(xnode)) + write(ASEmitterTokens.SPACE); + + getWalker().walk(node.getStatementContentsNode()); + } + + @Override + public void emitSwitch(ISwitchNode node) + { + writeToken(ASEmitterTokens.SWITCH); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getChild(0)); + writeToken(ASEmitterTokens.PAREN_CLOSE); + writeNewline(ASEmitterTokens.BLOCK_OPEN, true); + + IConditionalNode[] cnodes = ASNodeUtils.getCaseNodes(node); + ITerminalNode dnode = ASNodeUtils.getDefaultNode(node); + + for (int i = 0; i < cnodes.length; i++) + { + IConditionalNode casen = cnodes[i]; + IContainerNode cnode = (IContainerNode) casen.getChild(1); + writeToken(ASEmitterTokens.CASE); + getWalker().walk(casen.getConditionalExpressionNode()); + write(ASEmitterTokens.COLON); + if (!isImplicit(cnode)) + write(ASEmitterTokens.SPACE); + getWalker().walk(casen.getStatementContentsNode()); + if (i == cnodes.length - 1 && dnode == null) + { + indentPop(); + writeNewline(); + } + else + writeNewline(); + } + if (dnode != null) + { + IContainerNode cnode = (IContainerNode) dnode.getChild(0); + write(ASEmitterTokens.DEFAULT); + write(ASEmitterTokens.COLON); + if (!isImplicit(cnode)) + write(ASEmitterTokens.SPACE); + getWalker().walk(dnode); + indentPop(); + writeNewline(); + } + write(ASEmitterTokens.BLOCK_CLOSE); + } + + @Override + public void emitWhileLoop(IWhileLoopNode node) + { + IContainerNode cnode = (IContainerNode) node.getChild(1); + writeToken(ASEmitterTokens.WHILE); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getConditionalExpressionNode()); + write(ASEmitterTokens.PAREN_CLOSE); + if (!isImplicit(cnode)) + write(ASEmitterTokens.SPACE); + getWalker().walk(node.getStatementContentsNode()); + } + + @Override + public void emitDoLoop(IWhileLoopNode node) + { + IContainerNode cnode = (IContainerNode) node.getChild(0); + write(ASEmitterTokens.DO); + if (!isImplicit(cnode)) + write(ASEmitterTokens.SPACE); + getWalker().walk(node.getStatementContentsNode()); + if (!isImplicit(cnode)) + write(ASEmitterTokens.SPACE); + else + writeNewline(); // TODO (mschmalle) there is something wrong here, block should NL + write(ASEmitterTokens.WHILE); + write(ASEmitterTokens.SPACE); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getConditionalExpressionNode()); + write(ASEmitterTokens.PAREN_CLOSE); + write(ASEmitterTokens.SEMICOLON); + } + + @Override + public void emitWith(IWithNode node) + { + IContainerNode cnode = (IContainerNode) node.getChild(1); + writeToken(ASEmitterTokens.WITH); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getTargetNode()); + write(ASEmitterTokens.PAREN_CLOSE); + if (!isImplicit(cnode)) + write(ASEmitterTokens.SPACE); + getWalker().walk(node.getStatementContentsNode()); + } + + @Override + public void emitThrow(IThrowNode node) + { + writeToken(ASEmitterTokens.THROW); + getWalker().walk(node.getThrownExpressionNode()); + } + + @Override + public void emitTry(ITryNode node) + { + writeToken(ASEmitterTokens.TRY); + getWalker().walk(node.getStatementContentsNode()); + for (int i = 0; i < node.getCatchNodeCount(); i++) + { + getWalker().walk(node.getCatchNode(i)); + } + ITerminalNode fnode = node.getFinallyNode(); + if (fnode != null) + { + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.FINALLY); + getWalker().walk(fnode); + } + } + + @Override + public void emitCatch(ICatchNode node) + { + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.CATCH); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getCatchParameterNode()); + writeToken(ASEmitterTokens.PAREN_CLOSE); + getWalker().walk(node.getStatementContentsNode()); + } + + @Override + public void emitReturn(IReturnNode node) + { + write(ASEmitterTokens.RETURN); + IExpressionNode rnode = node.getReturnValueNode(); + if (rnode != null && rnode.getNodeID() != ASTNodeID.NilID) + { + write(ASEmitterTokens.SPACE); + getWalker().walk(rnode); + } + } + + //-------------------------------------------------------------------------- + // Expressions + //-------------------------------------------------------------------------- + + @Override + public void emitFunctionCall(IFunctionCallNode node) + { + if (node.isNewExpression()) + { + writeToken(ASEmitterTokens.NEW); + } + + getWalker().walk(node.getNameNode()); + + emitArguments(node.getArgumentsNode()); + } + + @Override + public void emitArguments(IContainerNode node) + { + write(ASEmitterTokens.PAREN_OPEN); + int len = node.getChildCount(); + for (int i = 0; i < len; i++) + { + IExpressionNode argumentNode = (IExpressionNode) node.getChild(i); + getWalker().walk(argumentNode); + if (i < len - 1) + { + writeToken(ASEmitterTokens.COMMA); + } + } + write(ASEmitterTokens.PAREN_CLOSE); + } + + //-------------------------------------------------------------------------- + // Operators + //-------------------------------------------------------------------------- + + @Override + public void emitAsOperator(IBinaryOperatorNode node) + { + getWalker().walk(node.getLeftOperandNode()); + write(ASEmitterTokens.SPACE); + writeToken(node.getOperator().getOperatorText()); + getWalker().walk(node.getRightOperandNode()); + } + + @Override + public void emitIsOperator(IBinaryOperatorNode node) + { + getWalker().walk(node.getLeftOperandNode()); + write(ASEmitterTokens.SPACE); + writeToken(node.getOperator().getOperatorText()); + getWalker().walk(node.getRightOperandNode()); + } + + @Override + public void emitBinaryOperator(IBinaryOperatorNode node) + { + if (ASNodeUtils.hasParenOpen(node)) + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getLeftOperandNode()); + if (node.getNodeID() != ASTNodeID.Op_CommaID) + write(ASEmitterTokens.SPACE); + writeToken(node.getOperator().getOperatorText()); + getWalker().walk(node.getRightOperandNode()); + if (ASNodeUtils.hasParenClose(node)) + write(ASEmitterTokens.PAREN_CLOSE); + } + + //-------------------------------------------------------------------------- + // Utility + //-------------------------------------------------------------------------- + + protected ITypeNode findTypeNode(IPackageNode node) + { + IScopedNode scope = node.getScopedNode(); + for (int i = 0; i < scope.getChildCount(); i++) + { + IASNode child = scope.getChild(i); + if (child instanceof ITypeNode) + return (ITypeNode) child; + } + return null; + } + + protected ITypeDefinition findType(Collection<IDefinition> definitions) + { + for (IDefinition definition : definitions) + { + if (definition instanceof ITypeDefinition) + return (ITypeDefinition) definition; + } + return null; + } + + //-------------------------------------------------------------------------- + // Static Utility + //-------------------------------------------------------------------------- + + protected static IFunctionNode getConstructor(IDefinitionNode[] members) + { + for (IDefinitionNode node : members) + { + if (node instanceof IFunctionNode) + { + IFunctionNode fnode = (IFunctionNode) node; + if (fnode.isConstructor()) + return fnode; + } + } + return null; + } + + protected static boolean isLastStatement(IASNode node) + { + return getChildIndex(node.getParent(), node) == node.getParent() + .getChildCount() - 1; + } + + // this is not fair that we have to do this if (i < len - 1) + private static int getChildIndex(IASNode parent, IASNode node) + { + final int len = parent.getChildCount(); + for (int i = 0; i < len; i++) + { + if (parent.getChild(i) == node) + return i; + } + return -1; + } + + protected static final boolean isImplicit(IContainerNode node) + { + return EmitterUtils.isImplicit(node); + } + + protected void visitForBody(IContainerNode node) + { + final IASNode node0 = node.getChild(0); + final IASNode node1 = node.getChild(1); + final IASNode node2 = node.getChild(2); + + // initializer + if (node0 != null) + { + getWalker().walk(node0); + write(ASEmitterTokens.SEMICOLON); + if (node1.getNodeID() != ASTNodeID.NilID) + write(ASEmitterTokens.SPACE); + } + // condition or target + if (node1 != null) + { + getWalker().walk(node1); + write(ASEmitterTokens.SEMICOLON); + if (node2.getNodeID() != ASTNodeID.NilID) + write(ASEmitterTokens.SPACE); + } + // iterator + if (node2 != null) + { + getWalker().walk(node2); + } + } + + @Override + public void emitLiteral(ILiteralNode node) + { + write(node.getValue(true)); + } + + @Override + public void emitLiteralContainer(ILiteralContainerNode node) + { + final ContainerNode cnode = node.getContentsNode(); + final ContainerType type = cnode.getContainerType(); + String postFix = ""; + + if (type == ContainerType.BRACES) + { + write(ASEmitterTokens.BLOCK_OPEN); + postFix = ASEmitterTokens.BLOCK_CLOSE.getToken(); + } + else if (type == ContainerType.BRACKETS) + { + write(ASEmitterTokens.SQUARE_OPEN); + postFix = ASEmitterTokens.SQUARE_CLOSE.getToken(); + } + else if (type == ContainerType.IMPLICIT) + { + // nothing to write, move along + } + else if (type == ContainerType.PARENTHESIS) + { + write(ASEmitterTokens.PAREN_OPEN); + postFix = ASEmitterTokens.PAREN_CLOSE.getToken(); + } + + final int len = cnode.getChildCount(); + for (int i = 0; i < len; i++) + { + IASNode child = cnode.getChild(i); + getWalker().walk(child); + if (i < len - 1) + writeToken(ASEmitterTokens.COMMA); + } + + if (postFix != "") + write(postFix); + } + + @Override + public void emitIdentifier(IIdentifierNode node) + { + write(node.getName()); + } + + @Override + public void emitNumericLiteral(INumericLiteralNode node) + { + write(node.getNumericValue().toString()); + } + + @Override + public void emitKeyword(IKeywordNode node) + { + write(node.getNodeID().getParaphrase()); + } + + @Override + public void emitIterationFlow(IIterationFlowNode node) + { + write(node.getKind().toString().toLowerCase()); + IIdentifierNode lnode = node.getLabelNode(); + if (lnode != null) + { + write(ASEmitterTokens.SPACE); + getWalker().walk(lnode); + } + } + + @Override + public void emitMemberAccessExpression(IMemberAccessExpressionNode node) + { + getWalker().walk(node.getLeftOperandNode()); + write(node.getOperator().getOperatorText()); + getWalker().walk(node.getRightOperandNode()); + } + + @Override + public void emitDynamicAccess(IDynamicAccessNode node) + { + getWalker().walk(node.getLeftOperandNode()); + write(ASEmitterTokens.SQUARE_OPEN); + getWalker().walk(node.getRightOperandNode()); + write(ASEmitterTokens.SQUARE_CLOSE); + } + + @Override + public void emitTypedExpression(ITypedExpressionNode node) + { + getWalker().walk(node.getCollectionNode()); + write(ASEmitterTokens.MEMBER_ACCESS); + write(ASEmitterTokens.LESS_THAN); + getWalker().walk(node.getTypeNode()); + write(ASEmitterTokens.GREATER_THAN); + } + + @Override + public void emitVariableExpression(IVariableExpressionNode node) + { + getWalker().walk(node.getTargetVariable()); + } + + @Override + public void emitTernaryOperator(ITernaryOperatorNode node) + { + if (ASNodeUtils.hasParenOpen((IOperatorNode) node)) + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getConditionalNode()); + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.TERNARY); + getWalker().walk(node.getLeftOperandNode()); + write(ASEmitterTokens.SPACE); + writeToken(ASEmitterTokens.COLON); + getWalker().walk(node.getRightOperandNode()); + if (ASNodeUtils.hasParenClose((IOperatorNode) node)) + write(ASEmitterTokens.PAREN_CLOSE); + } + + @Override + public void emitObjectLiteralValuePair(IObjectLiteralValuePairNode node) + { + getWalker().walk(node.getNameNode()); + write(ASEmitterTokens.COLON); + getWalker().walk(node.getValueNode()); + } + + @Override + public void emitLabelStatement(LabeledStatementNode node) + { + writeToken(node.getLabel()); + writeToken(ASEmitterTokens.COLON); + getWalker().walk(node.getLabeledStatement()); + } + + @Override + public void emitNamespaceAccessExpression( + INamespaceAccessExpressionNode node) + { + getWalker().walk(node.getLeftOperandNode()); + write(node.getOperator().getOperatorText()); + getWalker().walk(node.getRightOperandNode()); + } + + @Override + public void emitUnaryOperator(IUnaryOperatorNode node) + { + if (ASNodeUtils.hasParenOpen(node)) + write(ASEmitterTokens.PAREN_OPEN); + + if (node.getNodeID() == ASTNodeID.Op_PreIncrID + || node.getNodeID() == ASTNodeID.Op_PreDecrID + || node.getNodeID() == ASTNodeID.Op_BitwiseNotID + || node.getNodeID() == ASTNodeID.Op_LogicalNotID + || node.getNodeID() == ASTNodeID.Op_SubtractID + || node.getNodeID() == ASTNodeID.Op_AddID) + { + write(node.getOperator().getOperatorText()); + IExpressionNode opNode = node.getOperandNode(); + getWalker().walk(opNode); + } + + else if (node.getNodeID() == ASTNodeID.Op_PostIncrID + || node.getNodeID() == ASTNodeID.Op_PostDecrID) + { + getWalker().walk(node.getOperandNode()); + write(node.getOperator().getOperatorText()); + } + else if (node.getNodeID() == ASTNodeID.Op_DeleteID + || node.getNodeID() == ASTNodeID.Op_VoidID) + { + writeToken(node.getOperator().getOperatorText()); + getWalker().walk(node.getOperandNode()); + } + else if (node.getNodeID() == ASTNodeID.Op_TypeOfID) + { + write(node.getOperator().getOperatorText()); + write(ASEmitterTokens.PAREN_OPEN); + getWalker().walk(node.getOperandNode()); + write(ASEmitterTokens.PAREN_CLOSE); + } + + if (ASNodeUtils.hasParenClose(node)) + write(ASEmitterTokens.PAREN_CLOSE); + } + + @Override + public void emitLanguageIdentifier(ILanguageIdentifierNode node) + { + if (node.getKind() == ILanguageIdentifierNode.LanguageIdentifierKind.ANY_TYPE) + { + write(ASEmitterTokens.ANY_TYPE); + } + else if (node.getKind() == ILanguageIdentifierNode.LanguageIdentifierKind.REST) + { + write(ASEmitterTokens.ELLIPSIS); + } + else if (node.getKind() == ILanguageIdentifierNode.LanguageIdentifierKind.SUPER) + { + write(ASEmitterTokens.SUPER); + } + else if (node.getKind() == ILanguageIdentifierNode.LanguageIdentifierKind.THIS) + { + write(ASEmitterTokens.THIS); + } + else if (node.getKind() == ILanguageIdentifierNode.LanguageIdentifierKind.VOID) + { + write(ASEmitterTokens.VOID); + } + } + + @Override + public void emitMetaTag(IMetaTagNode node) + { + } + + @Override + public void emitContainer(IContainerNode node) + { + } + + @Override + public void emitE4XFilter(IMemberAccessExpressionNode node) + { + // ToDo (erikdebruin) + } + + @Override + public void emitUseNamespace(IUseNamespaceNode node) + { + // ToDo (erikdebruin) + } + + @Override ++ public void emitBlockOpen(IContainerNode node) ++ { ++ write(ASEmitterTokens.BLOCK_OPEN); ++ } ++ ++ @Override ++ public void emitBlockClose(IContainerNode node) ++ { ++ write(ASEmitterTokens.BLOCK_CLOSE); ++ } ++ ++ @Override + public String stringifyNode(IASNode node) + { + boolean oldBufferWrite = isBufferWrite(); + StringBuilder oldBuilder = this.builder; + this.builder = new StringBuilder(); + setBufferWrite(true); + getWalker().walk(node); + String result = getBuilder().toString(); + getBuilder().setLength(0); + this.builder = oldBuilder; + setBufferWrite(oldBufferWrite); + return result; + } +}
