blob: d6a97453d5d3990ecbab9e31f8491cf0b6f5ed2f [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.IASGlobalFunctionConstants;
import org.apache.royale.compiler.codegen.IDocEmitter;
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.constants.IASLanguageConstants;
import org.apache.royale.compiler.constants.IMetaAttributeConstants;
import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType;
import org.apache.royale.compiler.definitions.IAppliedVectorDefinition;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.metadata.IMetaTag;
import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute;
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.codegen.js.royale.JSRoyaleDocEmitter;
import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens;
import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
import org.apache.royale.compiler.internal.semantics.SemanticUtils;
import org.apache.royale.compiler.internal.tree.as.*;
import org.apache.royale.compiler.projects.ICompilerProject;
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.IExpressionNode;
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;
import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
import org.apache.royale.compiler.utils.NativeUtils;
/**
* @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);
String name = fnode.getName();
//may have a name, or may be anonymous
if(name.length() > 0)
{
write(ASEmitterTokens.SPACE);
write(name);
}
endMapping(node);
emitParameters(fnode.getParametersContainerNode());
emitFunctionScope(fnode.getScopedNode());
}
public void emitClosureStart()
{
}
public void emitClosureEnd(IASNode node, IDefinition nodeDef)
{
}
public void emitSourceMapDirective(ITypeNode node)
{
sourceMapDirectiveEmitter.isExterns = getModel().isExterns;
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;
}
}
}
//prefer forward slash
sourcePath = sourcePath.replace('\\', '/');
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;
}
public void emitAssignmentCoercion(IExpressionNode assignedNode, IDefinition definition)
{
IDefinition assignedDef = null;
IDefinition assignedTypeDef = null;
ICompilerProject project = getWalker().getProject();
if (assignedNode != null)
{
assignedDef = assignedNode.resolve(project);
assignedTypeDef = assignedNode.resolveType(project);
if (project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef))
{
IDefinition resolvedXMLDef = SemanticUtils.resolveXML(assignedNode, project);
if (resolvedXMLDef != null)
{
assignedDef = resolvedXMLDef;
assignedTypeDef = SemanticUtils.resolveTypeXML(assignedNode, project);
}
}
}
String coercionStart = null;
String coercionEnd = null;
boolean avoidCoercion = false;
if (project.getBuiltinType(BuiltinType.INT).equals(definition))
{
boolean needsCoercion = false;
if (assignedNode instanceof INumericLiteralNode)
{
INumericLiteralNode numericLiteral = (INumericLiteralNode) assignedNode;
INumericLiteralNode.INumericValue numericValue = numericLiteral.getNumericValue();
startMapping(assignedNode);
if(numericValue.toString().startsWith("0x"))
{
//for readability, keep the same formatting
write("0x" + Integer.toHexString(numericValue.toInt32()));
}
else
{
write(Integer.toString(numericValue.toInt32()));
}
endMapping(assignedNode);
return;
}
else if(assignedNode instanceof BinaryOperatorAsNode)
{
needsCoercion = true;
}
else if(!project.getBuiltinType(BuiltinType.INT).equals(assignedTypeDef))
{
needsCoercion = true;
}
if (needsCoercion)
{
coercionStart = "(";
coercionEnd = ") >> 0";
}
}
else if (project.getBuiltinType(BuiltinType.UINT).equals(definition))
{
boolean needsCoercion = false;
if (assignedNode instanceof INumericLiteralNode)
{
INumericLiteralNode numericLiteral = (INumericLiteralNode) assignedNode;
INumericLiteralNode.INumericValue numericValue = numericLiteral.getNumericValue();
startMapping(assignedNode);
if(numericValue.toString().startsWith("0x"))
{
//for readability, keep the same formatting
write("0x" + Long.toHexString(numericValue.toUint32()));
}
else
{
write(Long.toString(numericValue.toUint32()));
}
endMapping(assignedNode);
return;
}
else if(assignedNode instanceof BinaryOperatorAsNode)
{
needsCoercion = true;
}
else if(!project.getBuiltinType(BuiltinType.UINT).equals(assignedTypeDef))
{
needsCoercion = true;
}
if (needsCoercion)
{
coercionStart = "(";
coercionEnd = ") >>> 0";
}
}
else if (project.getBuiltinType(BuiltinType.NUMBER).equals(definition)
&& !project.getBuiltinType(BuiltinType.NUMBER).equals(assignedTypeDef)
&& !project.getBuiltinType(BuiltinType.INT).equals(assignedTypeDef)
&& !project.getBuiltinType(BuiltinType.UINT).equals(assignedTypeDef))
{
boolean needsCoercion = true;
if (assignedNode instanceof IDynamicAccessNode)
{
IDynamicAccessNode dynamicAccess = (IDynamicAccessNode) assignedNode;
IDefinition dynamicAccessIndexDef = dynamicAccess.getRightOperandNode().resolveType(project);
if (project.getBuiltinType(BuiltinType.NUMBER).equals(dynamicAccessIndexDef))
{
IDefinition leftDef = dynamicAccess.getLeftOperandNode().resolveType(project);
IMetaTag[] metas = leftDef.getAllMetaTags();
for (IMetaTag meta : metas)
{
if (meta.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_ARRAYELEMENTTYPE))
{
IMetaTagAttribute[] attrs = meta.getAllAttributes();
for (IMetaTagAttribute attr : attrs)
{
String t = attr.getValue();
if (t.equals(IASLanguageConstants.Number))
{
needsCoercion = false;
//explicitly prevent other coercion detection rules from picking this up
avoidCoercion = true;
}
}
}
}
}
}
if (needsCoercion)
{
coercionStart = "Number(";
}
}
else if (project.getBuiltinType(BuiltinType.BOOLEAN).equals(definition)
&& !project.getBuiltinType(BuiltinType.BOOLEAN).equals(assignedTypeDef))
{
if (project.getBuiltinType(BuiltinType.NULL).equals(assignedTypeDef)
|| (assignedDef != null && assignedDef.getQualifiedName().equals(IASLanguageConstants.UNDEFINED)))
{
//null and undefined are coerced to false
startMapping(assignedNode);
write(IASLanguageConstants.FALSE);
endMapping(assignedNode);
return;
}
if (assignedNode instanceof INumericLiteralNode)
{
INumericLiteralNode numericLiteral = (INumericLiteralNode) assignedNode;
INumericLiteralNode.INumericValue numericValue = numericLiteral.getNumericValue();
//zero is coerced to false, and everything else is true
String booleanValue = numericValue.toNumber() == 0.0
? IASLanguageConstants.FALSE
: IASLanguageConstants.TRUE;
startMapping(assignedNode);
write(booleanValue);
endMapping(assignedNode);
return;
}
coercionStart = "!!(";
}
else if (project.getBuiltinType(BuiltinType.STRING).equals(definition)
&& !project.getBuiltinType(BuiltinType.STRING).equals(assignedTypeDef)
&& !project.getBuiltinType(BuiltinType.NULL).equals(assignedTypeDef)
&& !(project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef)
&& SemanticUtils.isToStringFunctionCall(assignedNode, project)))
{
if(assignedDef != null && assignedDef.getQualifiedName().equals(IASLanguageConstants.UNDEFINED))
{
//undefined is coerced to null
startMapping(assignedNode);
write(IASLanguageConstants.NULL);
endMapping(assignedNode);
return;
}
boolean emitStringCoercion = true;
IDocEmitter docEmitter = getDocEmitter();
if (docEmitter instanceof JSRoyaleDocEmitter)
{
JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter;
emitStringCoercion = royaleDocEmitter.emitStringConversions;
}
if (emitStringCoercion)
{
coercionStart = "org.apache.royale.utils.Language.string(";
}
}
if ( assignedDef != null
&& assignedDef instanceof IAppliedVectorDefinition
&& assignedNode instanceof TypedExpressionNode) {
//assign a Vector class as the assigned value, e.g. var c:Class = Vector.<int>
if (project instanceof RoyaleJSProject
&& ((RoyaleJSProject)project).config.getJsVectorEmulationClass() != null) {
startMapping(assignedNode);
write(((RoyaleJSProject)project).config.getJsVectorEmulationClass());
endMapping(assignedNode);
} else {
startMapping(assignedNode);
write(JSRoyaleEmitterTokens.SYNTH_VECTOR);
write(ASEmitterTokens.PAREN_OPEN);
write(ASEmitterTokens.SINGLE_QUOTE);
//the element type of the Vector:
write(formatQualifiedName(((TypedExpressionNode)assignedNode).getTypeNode().resolve(project).getQualifiedName()));
write(ASEmitterTokens.SINGLE_QUOTE);
write(ASEmitterTokens.PAREN_CLOSE);
endMapping(assignedNode);
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
}
return;
}
if (assignedDef instanceof IClassDefinition
&& assignedNode instanceof IdentifierNode
&& ((IdentifierNode)assignedNode).getName().equals(IASGlobalFunctionConstants.Vector)
&& project instanceof RoyaleJSProject
&& ((RoyaleJSProject)project).config.getJsVectorEmulationClass() == null ){
startMapping(assignedNode);
write(JSRoyaleEmitterTokens.SYNTH_VECTOR);
write(ASEmitterTokens.PAREN_OPEN);
//null to signify not a valid constructor
write(ASEmitterTokens.NULL);
write(ASEmitterTokens.PAREN_CLOSE);
endMapping(assignedNode);
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
return;
}
if (coercionStart == null
&& !avoidCoercion
&& assignedTypeDef !=null
&& definition !=null
&& (project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef)
|| project.getBuiltinType(BuiltinType.OBJECT).equals(assignedTypeDef))
&& !(project.getBuiltinType(BuiltinType.ANY_TYPE).equals(definition)
|| project.getBuiltinType(BuiltinType.OBJECT).equals(definition))) {
//catch leftovers: remaining implicit coercion of loosely typed assigned values to strongly typed context
//assignment to Class definitions is excluded because there is no 'Class' type in JS
//Possibility: 'Class' could be implemented as a synthType
boolean needsCoercion = ((RoyaleJSProject)project).config.getJsComplexImplicitCoercions();
IDocEmitter docEmitter = getDocEmitter();
if (docEmitter instanceof JSRoyaleDocEmitter)
{
JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter;
//check for local toggle
if (needsCoercion) needsCoercion = !(royaleDocEmitter.getLocalSettingAsBoolean(
JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION, false));
else {
if (royaleDocEmitter.hasLocalSetting(JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION.getToken())) {
needsCoercion = !(royaleDocEmitter.getLocalSettingAsBoolean(
JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION, false));
}
}
if (needsCoercion) {
//check for individual specified suppression
String definitionName = definition.getQualifiedName();
//for Vectors, use the unqualified name to match the source code
if (NativeUtils.isVector(definitionName)) {
definitionName = definition.getBaseName();
}
if (royaleDocEmitter.getLocalSettingIncludesString(
JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION,
definitionName))
{
needsCoercion = false;
}
}
}
//Avoid specific compile-time 'fake' class(es)
if (needsCoercion && definition.getQualifiedName().equals("org.apache.royale.core.WrappedHTMLElement")) {
//*actual* coercion fails here, because this is not actually instantiated, it is
//simply a type definition representing the 'wrapped' (or tagged) HTMLElement
needsCoercion = false;
}
//Avoid XML/XMLList:
if (needsCoercion && project.getBuiltinType(BuiltinType.XML) != null) {
if (project.getBuiltinType(BuiltinType.XML).equals(definition)
|| project.getBuiltinType(BuiltinType.XMLLIST).equals(definition)) {
//XML/XMLList has complex output and would need more work
needsCoercion = false;
}
}
//avoid scenario with ArrayElementType specified as metadata definition type - assume it is 'typed'
if (needsCoercion && assignedNode instanceof IDynamicAccessNode)
{
IDynamicAccessNode dynamicAccess = (IDynamicAccessNode) assignedNode;
IDefinition dynamicAccessIndexDef = dynamicAccess.getRightOperandNode().resolveType(project);
if (project.getBuiltinType(BuiltinType.NUMBER).equals(dynamicAccessIndexDef))
{
IDefinition leftDef = dynamicAccess.getLeftOperandNode().resolveType(project);
if (leftDef != null) {
IMetaTag[] metas = leftDef.getAllMetaTags();
for (IMetaTag meta : metas)
{
if (meta.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_ARRAYELEMENTTYPE))
{
IMetaTagAttribute[] attrs = meta.getAllAttributes();
for (IMetaTagAttribute attr : attrs)
{
String t = attr.getValue();
if (t.equals(definition.getQualifiedName()))
{
needsCoercion = false;
}
}
}
}
}
}
}
if (needsCoercion && project.getBuiltinType(BuiltinType.STRING).equals(definition)) {
//explicit suppression of String coercion
if (docEmitter instanceof JSRoyaleDocEmitter)
{
JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter;
needsCoercion = royaleDocEmitter.emitStringConversions;
}
if (needsCoercion
&& assignedNode instanceof FunctionCallNode
&& ((FunctionCallNode) assignedNode).getNameNode() instanceof MemberAccessExpressionNode
&& ((MemberAccessExpressionNode)((FunctionCallNode) assignedNode).getNameNode()).getRightOperandNode() instanceof IdentifierNode
&& ((IdentifierNode)(((MemberAccessExpressionNode)((FunctionCallNode) assignedNode).getNameNode()).getRightOperandNode())).getName().equals("toString")) {
//even if toString() is called in an untyped way, assume a call to a method named 'toString' is actually providing a String
needsCoercion = false;
}
}
if (needsCoercion) {
//add a comment tag leader, so implicit casts are identifiable in the output
coercionStart = "/* implicit cast */ "
+ JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken()
+ ASEmitterTokens.MEMBER_ACCESS.getToken()
+ ASEmitterTokens.AS.getToken()
+ ASEmitterTokens.PAREN_OPEN.getToken();
String coercionTypeString = formatQualifiedName(definition.getQualifiedName());
if (NativeUtils.isSyntheticJSType(coercionTypeString)) {
String synthCall;
String synthethicType;
if (NativeUtils.isVector(coercionTypeString)) {
synthCall = JSRoyaleEmitterTokens.SYNTH_VECTOR.getToken();
synthethicType = formatQualifiedName(coercionTypeString.substring(8, coercionTypeString.length() -1));
} else {
synthCall = JSRoyaleEmitterTokens.SYNTH_TYPE.getToken();
synthethicType = coercionTypeString;
}
coercionTypeString = synthCall
+ ASEmitterTokens.PAREN_OPEN.getToken()
+ ASEmitterTokens.SINGLE_QUOTE.getToken()
+ synthethicType
+ ASEmitterTokens.SINGLE_QUOTE.getToken()
+ ASEmitterTokens.PAREN_CLOSE.getToken();
}
coercionEnd = ASEmitterTokens.COMMA.getToken()
+ ASEmitterTokens.SPACE.getToken()
+ coercionTypeString
+ ASEmitterTokens.COMMA.getToken()
+ ASEmitterTokens.SPACE.getToken()
+ ASEmitterTokens.TRUE.getToken()
+ ASEmitterTokens.PAREN_CLOSE.getToken();
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
}
}
if (coercionStart != null)
{
write(coercionStart);
}
emitAssignedValue(assignedNode);
if (coercionStart != null)
{
if (coercionEnd != null)
{
write(coercionEnd);
}
else
{
write(")");
}
}
}
}