blob: d3a65abdd02bd2d5c8b6b71247bcca3dd60c6505 [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.royale;
import java.io.File;
import java.io.FilterWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.royale.compiler.codegen.js.royale.IJSRoyaleEmitter;
import org.apache.royale.compiler.codegen.js.goog.IJSGoogDocEmitter;
import org.apache.royale.compiler.constants.IASKeywordConstants;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.constants.INamespaceConstants;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
import org.apache.royale.compiler.definitions.IPackageDefinition;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.definitions.IVariableDefinition;
import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute;
import org.apache.royale.compiler.definitions.references.INamespaceResolvedReference;
import org.apache.royale.compiler.embedding.EmbedAttribute;
import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
import org.apache.royale.compiler.internal.codegen.js.JSSessionModel.ImplicitBindableImplementation;
import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitter;
import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitterTokens;
import org.apache.royale.compiler.internal.codegen.js.jx.AccessorEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.AsIsEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.BinaryOperatorEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.BindableEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ClassEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.DefinePropertyFunctionEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.FieldEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ForEachEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.FunctionCallEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.IdentifierEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.InterfaceEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.LiteralEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.MemberAccessEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.MethodEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.ObjectDefinePropertyEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.PackageFooterEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.PackageHeaderEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.SelfReferenceEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.SuperCallEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.VarDeclarationEmitter;
import org.apache.royale.compiler.internal.codegen.js.jx.BinaryOperatorEmitter.DatePropertiesGetters;
import org.apache.royale.compiler.internal.codegen.js.jx.BinaryOperatorEmitter.DatePropertiesSetters;
import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils;
import org.apache.royale.compiler.internal.codegen.mxml.royale.MXMLRoyaleEmitter;
import org.apache.royale.compiler.internal.definitions.AccessorDefinition;
import org.apache.royale.compiler.internal.definitions.FunctionDefinition;
import org.apache.royale.compiler.internal.definitions.VariableDefinition;
import org.apache.royale.compiler.internal.embedding.EmbedData;
import org.apache.royale.compiler.internal.embedding.EmbedMIMEType;
import org.apache.royale.compiler.internal.projects.CompilerProject;
import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
import org.apache.royale.compiler.internal.projects.RoyaleProject;
import org.apache.royale.compiler.internal.semantics.SemanticUtils;
import org.apache.royale.compiler.internal.tree.as.*;
import org.apache.royale.compiler.problems.EmbedUnableToReadSourceProblem;
import org.apache.royale.compiler.problems.FilePrivateItemsWithMainVarWarningProblem;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IAccessorNode;
import org.apache.royale.compiler.tree.as.IBinaryOperatorNode;
import org.apache.royale.compiler.tree.as.IClassNode;
import org.apache.royale.compiler.tree.as.IContainerNode;
import org.apache.royale.compiler.tree.as.IDefinitionNode;
import org.apache.royale.compiler.tree.as.IEmbedNode;
import org.apache.royale.compiler.tree.as.IExpressionNode;
import org.apache.royale.compiler.tree.as.IFileNode;
import org.apache.royale.compiler.tree.as.IForLoopNode;
import org.apache.royale.compiler.tree.as.IFunctionCallNode;
import org.apache.royale.compiler.tree.as.IFunctionNode;
import org.apache.royale.compiler.tree.as.IFunctionObjectNode;
import org.apache.royale.compiler.tree.as.IGetterNode;
import org.apache.royale.compiler.tree.as.IIdentifierNode;
import org.apache.royale.compiler.tree.as.IInterfaceNode;
import org.apache.royale.compiler.tree.as.ILiteralNode;
import org.apache.royale.compiler.tree.as.IMemberAccessExpressionNode;
import org.apache.royale.compiler.tree.as.INamespaceDecorationNode;
import org.apache.royale.compiler.tree.as.INamespaceNode;
import org.apache.royale.compiler.tree.as.IPackageNode;
import org.apache.royale.compiler.tree.as.IScopedNode;
import org.apache.royale.compiler.tree.as.ISetterNode;
import org.apache.royale.compiler.tree.as.ITypedExpressionNode;
import org.apache.royale.compiler.tree.as.IUnaryOperatorNode;
import org.apache.royale.compiler.tree.as.IVariableNode;
import org.apache.royale.compiler.units.ICompilationUnit;
import org.apache.royale.compiler.utils.ASNodeUtils;
import com.google.common.base.Joiner;
import org.apache.royale.compiler.utils.NativeUtils;
import org.apache.royale.utils.FilenameNormalization;
/**
* Concrete implementation of the 'Royale' JavaScript production.
*
* @author Michael Schmalle
* @author Erik de Bruin
*/
public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter
{
private JSRoyaleDocEmitter docEmitter = null;
private PackageHeaderEmitter packageHeaderEmitter;
public PackageFooterEmitter packageFooterEmitter;
private BindableEmitter bindableEmitter;
private ClassEmitter classEmitter;
private InterfaceEmitter interfaceEmitter;
private FieldEmitter fieldEmitter;
public VarDeclarationEmitter varDeclarationEmitter;
public AccessorEmitter accessorEmitter;
public MethodEmitter methodEmitter;
private FunctionCallEmitter functionCallEmitter;
private SuperCallEmitter superCallEmitter;
private ForEachEmitter forEachEmitter;
private MemberAccessEmitter memberAccessEmitter;
private BinaryOperatorEmitter binaryOperatorEmitter;
private IdentifierEmitter identifierEmitter;
private LiteralEmitter literalEmitter;
private AsIsEmitter asIsEmitter;
private SelfReferenceEmitter selfReferenceEmitter;
private ObjectDefinePropertyEmitter objectDefinePropertyEmitter;
private DefinePropertyFunctionEmitter definePropertyFunctionEmitter;
public ArrayList<String> usedNames = new ArrayList<String>();
public ArrayList<String> staticUsedNames = new ArrayList<String>();
private boolean needNamespace;
private Set<IFunctionNode> emittingHoistedNodes = new HashSet<IFunctionNode>();
@Override
public String postProcess(String output)
{
output = super.postProcess(output);
String[] lines = output.split("\n");
ArrayList<String> finalLines = new ArrayList<String>();
boolean foundLanguage = false;
boolean foundXML = false;
boolean foundNamespace = false;
boolean sawRequires = false;
boolean stillSearching = true;
int addIndex = -1;
int provideIndex = -1;
int len = lines.length;
for (int i = 0; i < len; i++)
{
String line = lines[i];
if (stillSearching)
{
if (provideIndex == -1 || !sawRequires)
{
int c = line.indexOf(JSGoogEmitterTokens.GOOG_PROVIDE.getToken());
if (c != -1)
{
// if zero requires are found, require Language after the
// call to goog.provide
provideIndex = addIndex = i + 1;
}
}
int c = line.indexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
if (c != -1)
{
// we found other requires, so we'll just add Language at
// the end of the list
addIndex = -1;
int c2 = line.indexOf(")");
String s = line.substring(c + 14, c2 - 1);
if (s.equals(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken()))
{
foundLanguage = true;
}
else if (s.equals(IASLanguageConstants.XML))
{
foundXML = true;
}
else if (s.equals(IASLanguageConstants.Namespace))
{
foundNamespace = true;
}
sawRequires = true;
/*
if (!usedNames.contains(s))
{
removeLineFromMappings(i);
continue;
}
*/
}
else if (sawRequires || i == len - 1)
{
stillSearching = false;
//when we emitted the requires based on the imports, we may
//not have known if Language was needed yet because the
//imports are at the beginning of the file. other code,
//later in the file, may require Language.
ICompilerProject project = getWalker().getProject();
if (project instanceof RoyaleJSProject)
{
RoyaleJSProject royaleProject = (RoyaleJSProject) project;
boolean needLanguage = getModel().needLanguage;
if (needLanguage && !foundLanguage)
{
StringBuilder appendString = new StringBuilder();
appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
appendString.append(ASEmitterTokens.PAREN_OPEN.getToken());
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken());
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken());
appendString.append(ASEmitterTokens.SEMICOLON.getToken());
if(addIndex != -1)
{
// if we didn't find other requires, this index
// points to the line after goog.provide
finalLines.add(addIndex, appendString.toString());
addLineToMappings(addIndex);
}
else
{
finalLines.add(appendString.toString());
addLineToMappings(i);
}
}
boolean needXML = royaleProject.needXML;
if (needXML && !foundXML)
{
StringBuilder appendString = new StringBuilder();
appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
appendString.append(ASEmitterTokens.PAREN_OPEN.getToken());
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(IASLanguageConstants.XML);
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken());
appendString.append(ASEmitterTokens.SEMICOLON.getToken());
if(addIndex != -1)
{
// if we didn't find other requires, this index
// points to the line after goog.provide
finalLines.add(addIndex, appendString.toString());
addLineToMappings(addIndex);
}
else
{
finalLines.add(appendString.toString());
addLineToMappings(i);
}
}
if (needNamespace && !foundNamespace)
{
StringBuilder appendString = new StringBuilder();
appendString.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
appendString.append(ASEmitterTokens.PAREN_OPEN.getToken());
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(IASLanguageConstants.Namespace);
appendString.append(ASEmitterTokens.SINGLE_QUOTE.getToken());
appendString.append(ASEmitterTokens.PAREN_CLOSE.getToken());
appendString.append(ASEmitterTokens.SEMICOLON.getToken());
if(addIndex != -1)
{
// if we didn't find other requires, this index
// points to the line after goog.provide
finalLines.add(addIndex, appendString.toString());
addLineToMappings(addIndex);
}
else
{
finalLines.add(appendString.toString());
addLineToMappings(i);
}
}
}
}
}
finalLines.add(line);
}
if (staticUsedNames.size() > 0)
{
StringBuilder sb = new StringBuilder();
sb.append(JSGoogEmitterTokens.ROYALE_STATIC_DEPENDENCY_LIST.getToken());
boolean firstDependency = true;
for (String staticName : staticUsedNames)
{
if (!firstDependency)
sb.append(",");
firstDependency = false;
sb.append(staticName);
}
sb.append("*/");
finalLines.add(provideIndex, sb.toString());
addLineToMappings(provideIndex);
}
return Joiner.on("\n").join(finalLines);
}
public BindableEmitter getBindableEmitter()
{
return bindableEmitter;
}
public FieldEmitter getFieldEmitter()
{
return fieldEmitter;
}
public ClassEmitter getClassEmitter()
{
return classEmitter;
}
public AccessorEmitter getAccessorEmitter()
{
return accessorEmitter;
}
public PackageFooterEmitter getPackageFooterEmitter()
{
return packageFooterEmitter;
}
// TODO (mschmalle) Fix; this is not using the backend doc strategy for replacement
@Override
public IJSGoogDocEmitter getDocEmitter()
{
if (docEmitter == null)
docEmitter = new JSRoyaleDocEmitter(this);
return docEmitter;
}
public JSRoyaleEmitter(FilterWriter out)
{
super(out);
packageHeaderEmitter = new PackageHeaderEmitter(this);
packageFooterEmitter = new PackageFooterEmitter(this);
bindableEmitter = new BindableEmitter(this);
classEmitter = new ClassEmitter(this);
interfaceEmitter = new InterfaceEmitter(this);
fieldEmitter = new FieldEmitter(this);
varDeclarationEmitter = new VarDeclarationEmitter(this);
accessorEmitter = new AccessorEmitter(this);
methodEmitter = new MethodEmitter(this);
functionCallEmitter = new FunctionCallEmitter(this);
superCallEmitter = new SuperCallEmitter(this);
forEachEmitter = new ForEachEmitter(this);
memberAccessEmitter = new MemberAccessEmitter(this);
binaryOperatorEmitter = new BinaryOperatorEmitter(this);
identifierEmitter = new IdentifierEmitter(this);
literalEmitter = new LiteralEmitter(this);
asIsEmitter = new AsIsEmitter(this);
selfReferenceEmitter = new SelfReferenceEmitter(this);
objectDefinePropertyEmitter = new ObjectDefinePropertyEmitter(this);
definePropertyFunctionEmitter = new DefinePropertyFunctionEmitter(this);
}
@Override
protected void writeIndent()
{
write(JSRoyaleEmitterTokens.INDENT);
}
@Override
protected String getIndent(int numIndent)
{
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < numIndent; i++)
sb.append(JSRoyaleEmitterTokens.INDENT.getToken());
return sb.toString();
}
@Override
public void emitLocalNamedFunction(IFunctionNode node)
{
IFunctionNode parentFnNode = (IFunctionNode) node.getAncestorOfType(IFunctionNode.class);
if (parentFnNode == null || isEmittingHoistedNodes(parentFnNode))
{
// emit named functions only when allowed
super.emitLocalNamedFunction(node);
}
}
public Boolean isEmittingHoistedNodes(IFunctionNode node)
{
return emittingHoistedNodes.contains(node);
}
protected void setEmittingHoistedNodes(IFunctionNode node, boolean emit)
{
if (emit)
{
emittingHoistedNodes.add(node);
}
else
{
emittingHoistedNodes.remove(node);
}
}
@Override
public void emitFunctionBlockHeader(IFunctionNode node)
{
setEmittingHoistedNodes(node, true);
super.emitFunctionBlockHeader(node);
if (node.isConstructor())
{
IClassNode cnode = (IClassNode) node.getAncestorOfType(IClassNode.class);
if (cnode.getDefinition().needsEventDispatcher(getWalker().getProject())) {
//add whatever variant of the implementation is necessary inside the constructor
if (getModel().getImplicitBindableImplementation() == ImplicitBindableImplementation.IMPLEMENTS)
bindableEmitter.emitBindableImplementsConstructorCode();
else if (getModel().getImplicitBindableImplementation() == ImplicitBindableImplementation.EXTENDS)
bindableEmitter.emitBindableExtendsConstructorCode(cnode.getDefinition().getQualifiedName(),false);
}
emitComplexInitializers(cnode);
}
emitHoistedFunctionsCodeBlock(node);
if (!getModel().isExterns)
emitHoistedVariablesCodeBlock(node);
setEmittingHoistedNodes(node, false);
}
protected void emitHoistedFunctionsCodeBlock(IFunctionNode node)
{
if (!node.containsLocalFunctions())
{
return;
}
List<IFunctionNode> localFns = node.getLocalFunctions();
int n = localFns.size();
for (int i = 0; i < n; i++)
{
IFunctionNode localFn = localFns.get(i);
// named functions need to be declared at the top level to
// comply with JS strict mode.
// anonymous functions can be emitted inline, so we'll do that.
if (localFn.getName().length() > 0)
{
getWalker().walk(localFn);
write(ASEmitterTokens.SEMICOLON);
this.writeNewline();
}
}
}
protected void emitHoistedVariablesCodeBlock(IFunctionNode node)
{
boolean defaultInitializers = false;
ICompilerProject project = getWalker().getProject();
if(project instanceof RoyaleJSProject)
{
RoyaleJSProject fjsProject = (RoyaleJSProject) project;
if(fjsProject.config != null)
{
defaultInitializers = fjsProject.config.getJsDefaultInitializers();
}
}
Collection<IDefinition> localDefs = node.getScopedNode().getScope().getAllLocalDefinitions();
for (IDefinition localDef : localDefs)
{
if (localDef instanceof IVariableDefinition)
{
IVariableDefinition varDef = (IVariableDefinition) localDef;
IVariableNode varNode = varDef.getVariableNode();
if (varNode == null)
{
//no associated node is possible for implicit variables,
//like "arguments"
continue;
}
if (varNode instanceof ChainedVariableNode)
{
//these will be handled from the first variable in the chain
continue;
}
if (EmitterUtils.needsDefaultValue(varNode, defaultInitializers, getWalker().getProject()))
{
emitVarDeclaration(varNode);
write(ASEmitterTokens.SEMICOLON);
writeNewline();
}
else
{
//when the first varible in a chain doesn't need a default
//value, we need to manually check the rest of the chain
int len = varNode.getChildCount();
for(int i = 0; i < len; i++)
{
IASNode child = varNode.getChild(i);
if(child instanceof ChainedVariableNode)
{
ChainedVariableNode childVarNode = (ChainedVariableNode) child;
if (EmitterUtils.needsDefaultValue(childVarNode, defaultInitializers, getWalker().getProject()))
{
writeToken(ASEmitterTokens.VAR);
emitVarDeclaration(childVarNode);
write(ASEmitterTokens.SEMICOLON);
writeNewline();
}
}
}
}
}
}
}
@Override
public void emitFunctionObject(IFunctionObjectNode node)
{
IFunctionNode parentFnNode = (IFunctionNode) node.getAncestorOfType(IFunctionNode.class);
IFunctionNode localFnNode = node.getFunctionNode();
if (parentFnNode == null || isEmittingHoistedNodes(parentFnNode))
{
// emit named functions only when allowed
super.emitFunctionObject(node);
}
else if(localFnNode.getName().length() == 0)
{
// anonymous functions are allowed to be inline
super.emitFunctionObject(node);
}
else
{
List<IFunctionNode> localFns = parentFnNode.getLocalFunctions();
int i = localFns.indexOf(node.getFunctionNode());
if (i < 0)
System.out.println("missing index for " + node.toString());
else
{
IFunctionNode localFn = localFns.get(i);
IExpressionNode idNode = localFn.getNameExpressionNode();
String fnName = "";
if (idNode != null && idNode.getNodeID() == ASTNodeID.IdentifierID)
fnName = ((IdentifierNode)idNode).getName();
if (!fnName.isEmpty())
write(((IdentifierNode)idNode).getName());
else
write("__localFn" + Integer.toString(i) + "__");
}
}
}
@Override
public void emitNamespace(INamespaceNode node)
{
needNamespace = true;
write(formatQualifiedName(node.getQualifiedName()));
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
writeToken(ASEmitterTokens.NEW);
write(IASLanguageConstants.Namespace);
write(ASEmitterTokens.PAREN_OPEN);
if (!staticUsedNames.contains(IASLanguageConstants.Namespace))
staticUsedNames.add(IASLanguageConstants.Namespace);
IExpressionNode uriNode = node.getNamespaceURINode();
if (uriNode == null)
{
write(ASEmitterTokens.SINGLE_QUOTE);
write(node.getName());
write(ASEmitterTokens.SINGLE_QUOTE);
}
else
getWalker().walk(uriNode);
write(ASEmitterTokens.PAREN_CLOSE);
write(ASEmitterTokens.SEMICOLON);
}
public boolean isCustomNamespace(FunctionNode node)
{
INamespaceDecorationNode ns = ((FunctionNode)node).getActualNamespaceNode();
if (ns != null)
{
String nsName = node.getNamespace();
if (!(nsName == IASKeywordConstants.PRIVATE ||
nsName == IASKeywordConstants.PROTECTED ||
nsName == IASKeywordConstants.INTERNAL ||
nsName == INamespaceConstants.AS3URI ||
nsName == IASKeywordConstants.PUBLIC))
{
return true;
}
}
return false;
}
public boolean isCustomNamespace(FunctionDefinition def)
{
INamespaceDefinition nsDef = def.getNamespaceReference().resolveNamespaceReference(getWalker().getProject());
String uri = nsDef.getURI();
if (!def.getNamespaceReference().isLanguageNamespace() && !uri.equals(INamespaceConstants.AS3URI) &&
!nsDef.getBaseName().equals(ASEmitterTokens.PRIVATE.getToken()))
return true;
return false;
}
@Override
public void emitMemberName(IDefinitionNode node)
{
ICompilerProject project = getWalker().getProject();
if (node.getNodeID() == ASTNodeID.FunctionID)
{
FunctionNode fn = (FunctionNode)node;
if (isCustomNamespace(fn))
{
INamespaceDecorationNode ns = ((FunctionNode)node).getActualNamespaceNode();
INamespaceDefinition nsDef = (INamespaceDefinition)ns.resolve(project);
formatQualifiedName(nsDef.getQualifiedName()); // register with used names
String s = nsDef.getURI();
write(formatNamespacedProperty(s, node.getName(), true));
return;
}
}
String qname = node.getName();
IDefinition nodeDef = node.getDefinition();
if (nodeDef != null && !nodeDef.isStatic() && nodeDef.isPrivate() && project.getAllowPrivateNameConflicts())
qname = formatPrivateName(nodeDef.getParent().getQualifiedName(), qname);
write(qname);
}
public static String formatNamespacedProperty(String s, String propName, boolean access) {
//closure compiler choked on this syntax so stop using bracket notation for now
//if (access) return "[\"" + s + "::" + propName + "\"]";
//return "\"" + s + "::" + propName + "\"";
s = s.replace(":", "_");
s = s.replace(".", "_");
s = s.replace("/", "$");
s += "__" + propName;
if (access)
s = ASEmitterTokens.MEMBER_ACCESS.getToken() + s;
return s;
}
@Override
public String formatQualifiedName(String name)
{
return formatQualifiedName(name, false);
}
public MXMLRoyaleEmitter mxmlEmitter = null;
public String formatQualifiedName(String name, boolean isDoc)
{
if (mxmlEmitter != null)
name = mxmlEmitter.formatQualifiedName(name);
/*
if (name.contains("goog.") || name.startsWith("Vector."))
return name;
name = name.replaceAll("\\.", "_");
*/
if (getModel().isInternalClass(name))
return getModel().getInternalClasses().get(name);
if (NativeUtils.isJSNative(name)) return name;
if (name.startsWith("window."))
name = name.substring(7);
else if (!isDoc)
{
if (getModel().inStaticInitializer)
if (!staticUsedNames.contains(name) && !NativeUtils.isJSNative(name)
&& isGoogProvided(name) && !getModel().getCurrentClass().getQualifiedName().equals(name)
&& (getModel().primaryDefinitionQName == null
|| !getModel().primaryDefinitionQName.equals(name)))
staticUsedNames.add(name);
if (!usedNames.contains(name) && isGoogProvided(name))
usedNames.add(name);
}
return name;
}
public String convertASTypeToJS(String name)
{
String result = name;
if (name.equals(""))
result = IASLanguageConstants.Object;
else if (name.equals(IASLanguageConstants.Class))
result = IASLanguageConstants.Object;
else if (name.equals(IASLanguageConstants._int)
|| name.equals(IASLanguageConstants.uint))
result = IASLanguageConstants.Number;
else if (name.equals(IASLanguageConstants.Vector) || name.equals("__AS3__.vec.Vector")) {
result = JSRoyaleEmitterTokens.VECTOR.getToken();
}
return result;
}
//--------------------------------------------------------------------------
// Package Level
//--------------------------------------------------------------------------
@Override
public void emitPackageHeader(IPackageDefinition definition)
{
IPackageNode packageNode = definition.getNode();
IFileNode fileNode = (IFileNode) packageNode.getAncestorOfType(IFileNode.class);
int nodeCount = fileNode.getChildCount();
String mainClassName = null;
Boolean mainClassNameisVar = false;
IASNode firstInternalContent = null;
for (int i = 0; i < nodeCount; i++)
{
IASNode pnode = fileNode.getChild(i);
if (pnode instanceof IPackageNode)
{
IScopedNode snode = ((IPackageNode)pnode).getScopedNode();
int snodeCount = snode.getChildCount();
for (int j = 0; j < snodeCount; j++)
{
//there can only be one externally visible definition of either class, namespace, variable or function
//and package scope does not permit other class level access modifiers, otherwise a compiler error has already occurred.
//So mainClassName is derived from the first instance of any of these.
IASNode cnode = snode.getChild(j);
if (cnode instanceof IClassNode)
{
mainClassName = ((IClassNode)cnode).getQualifiedName();
break;
}
else if (cnode instanceof IFunctionNode)
{
mainClassName = ((IFunctionNode)cnode).getQualifiedName();
break;
}
else if (cnode instanceof INamespaceNode)
{
mainClassName = ((INamespaceNode)cnode).getQualifiedName();
break;
}
else if (cnode instanceof IVariableNode)
{
mainClassName = ((IVariableNode)cnode).getQualifiedName();
mainClassNameisVar = true;
break;
}
}
}
else if (pnode instanceof IClassNode)
{
String className = ((IClassNode)pnode).getQualifiedName();
getModel().getInternalClasses().put(className, mainClassName + "." + className);
if (firstInternalContent == null) firstInternalContent = pnode;
}
else if (pnode instanceof IInterfaceNode)
{
String className = ((IInterfaceNode)pnode).getQualifiedName();
getModel().getInternalClasses().put(className, mainClassName + "." + className);
if (firstInternalContent == null) firstInternalContent = pnode;
}
else if (pnode instanceof IFunctionNode)
{
String className = ((IFunctionNode)pnode).getQualifiedName();
getModel().getInternalClasses().put(className, mainClassName + "." + className);
if (firstInternalContent == null) firstInternalContent = pnode;
}
else if (pnode instanceof INamespaceNode)
{
String className = ((INamespaceNode)pnode).getQualifiedName();
getModel().getInternalClasses().put(className, mainClassName + "." + className);
if (firstInternalContent == null) firstInternalContent = pnode;
}
else if (pnode instanceof IVariableNode)
{
String className = ((IVariableNode)pnode).getQualifiedName();
getModel().getInternalClasses().put(className, mainClassName + "." + className);
if (firstInternalContent == null) firstInternalContent = pnode;
}
}
if (mainClassNameisVar && firstInternalContent != null) {
getProblems().add(new FilePrivateItemsWithMainVarWarningProblem(firstInternalContent));
}
packageHeaderEmitter.emit(definition);
}
@Override
public void emitPackageHeaderContents(IPackageDefinition definition)
{
packageHeaderEmitter.emitContents(definition);
usedNames.clear();
}
@Override
public void emitPackageFooter(IPackageDefinition definition)
{
packageFooterEmitter.emit(definition);
}
//--------------------------------------------------------------------------
// Class
//--------------------------------------------------------------------------
@Override
public void emitClass(IClassNode node)
{
classEmitter.emit(node);
}
@Override
public void emitInterface(IInterfaceNode node)
{
interfaceEmitter.emit(node);
}
@Override
public void emitField(IVariableNode node)
{
fieldEmitter.emit(node);
}
@Override
public void emitVarDeclaration(IVariableNode node)
{
varDeclarationEmitter.emit(node);
}
@Override
public void emitAccessors(IAccessorNode node)
{
accessorEmitter.emit(node);
}
@Override
public void emitGetAccessor(IGetterNode node)
{
accessorEmitter.emitGet(node);
}
@Override
public void emitSetAccessor(ISetterNode node)
{
accessorEmitter.emitSet(node);
}
@Override
public void emitMethod(IFunctionNode node)
{
methodEmitter.emit(node);
}
public void emitComplexInitializers(IClassNode node)
{
classEmitter.emitComplexInitializers(node);
}
//--------------------------------------------------------------------------
// Statements
//--------------------------------------------------------------------------
@Override
public void emitFunctionCall(IFunctionCallNode node)
{
functionCallEmitter.emit(node);
}
@Override
public void emitForEachLoop(IForLoopNode node)
{
forEachEmitter.emit(node);
}
//--------------------------------------------------------------------------
// Expressions
//--------------------------------------------------------------------------
@Override
public void emitSuperCall(IASNode node, String type)
{
superCallEmitter.emit(node, type);
}
@Override
public void emitMemberAccessExpression(IMemberAccessExpressionNode node)
{
memberAccessEmitter.emit(node);
}
@Override
public void emitArguments(IContainerNode node)
{
IContainerNode newNode = node;
int len = node.getChildCount();
if (len == 2)
{
ICompilerProject project = getWalker().getProject();;
IFunctionCallNode fcNode = (IFunctionCallNode) node.getParent();
IExpressionNode nameNode = fcNode.getNameNode();
IDefinition def = nameNode.resolve(project);
if (def != null && def.getBaseName().equals("insertAt"))
{
if (def.getParent() != null &&
def.getParent().getQualifiedName().equals("Array"))
{
if (nameNode instanceof MemberAccessExpressionNode)
{
newNode = EmitterUtils.insertArgumentsAt(node, 1, new NumericLiteralNode("0"));
}
}
}
}
if (len == 1)
{
ICompilerProject project = getWalker().getProject();;
IFunctionCallNode fcNode = (IFunctionCallNode) node.getParent();
IExpressionNode nameNode = fcNode.getNameNode();
IDefinition def = nameNode.resolve(project);
if (def != null && def.getBaseName().equals("removeAt"))
{
if (def.getParent() != null &&
def.getParent().getQualifiedName().equals("Array"))
{
if (nameNode instanceof MemberAccessExpressionNode)
{
newNode = EmitterUtils.insertArgumentsAfter(node, new NumericLiteralNode("1"));
}
}
}
else if (def != null && def.getBaseName().equals("parseInt"))
{
IDefinition parentDef = def.getParent();
if (parentDef == null)
{
if (nameNode instanceof IdentifierNode)
{
//see FLEX-35283
LiteralNode appendedArgument = new NumericLiteralNode("undefined");
appendedArgument.setSynthetic(true);
newNode = EmitterUtils.insertArgumentsAfter(node, appendedArgument);
}
}
}
}
super.emitArguments(newNode);
}
@Override
public void emitE4XFilter(IMemberAccessExpressionNode node)
{
getWalker().walk(node.getLeftOperandNode());
getModel().inE4xFilter = true;
write(".filter(function(node){return (");
String s = stringifyNode(node.getRightOperandNode());
if (s.startsWith("(") && s.endsWith(")"))
s = s.substring(1, s.length() - 1);
write(s);
write(")})");
getModel().inE4xFilter = false;
}
@Override
public void emitBinaryOperator(IBinaryOperatorNode node)
{
binaryOperatorEmitter.emit(node);
}
@Override
public void emitIdentifier(IIdentifierNode node)
{
identifierEmitter.emit(node);
}
@Override
public void emitLiteral(ILiteralNode node)
{
literalEmitter.emit(node);
}
@Override
public void emitEmbed(IEmbedNode node)
{
// if the embed is text/plain, return the actual text from the file.
// this assumes the variable being initialized is of type String.
// Embed node seems to not have location, so use parent.
EmbedData data = new EmbedData(node.getParent().getSourcePath(), null);
boolean hadError = false;
for (IMetaTagAttribute attribute : node.getAttributes())
{
String key = attribute.getKey();
String value = attribute.getValue();
if (data.addAttribute((CompilerProject) project, node.getParent(), key, value, getProblems()))
{
hadError = true;
}
}
if (hadError)
{
write("");
return;
}
String source = (String) data.getAttribute(EmbedAttribute.SOURCE);
EmbedMIMEType mimeType = (EmbedMIMEType) data.getAttribute(EmbedAttribute.MIME_TYPE);
if (mimeType != null && mimeType.toString().equals(EmbedMIMEType.TEXT.toString()) && source != null)
{
File file = new File(FilenameNormalization.normalize(source));
try {
String newlineReplacement = "\\\\n";
String s = FileUtils.readFileToString(file);
s = s.replaceAll("\n", "__NEWLINE_PLACEHOLDER__");
s = s.replaceAll("\r", "__CR_PLACEHOLDER__");
s = s.replaceAll("\t", "__TAB_PLACEHOLDER__");
s = s.replaceAll("\f", "__FORMFEED_PLACEHOLDER__");
s = s.replaceAll("\b", "__BACKSPACE_PLACEHOLDER__");
s = s.replaceAll("\\\\", "__ESCAPE_PLACEHOLDER__");
s = s.replaceAll("\\\\\"", "__QUOTE_PLACEHOLDER__");
s = s.replaceAll("\"", "\\\\\"");
s = s.replaceAll("__QUOTE_PLACEHOLDER__", "\\\\\"");
s = s.replaceAll("__ESCAPE_PLACEHOLDER__", "\\\\\\\\");
s = s.replaceAll("__BACKSPACE_PLACEHOLDER__", "\\\\b");
s = s.replaceAll("__FORMFEED_PLACEHOLDER__", "\\\\f");
s = s.replaceAll("__TAB_PLACEHOLDER__", "\\\\t");
s = s.replaceAll("__CR_PLACEHOLDER__", "\\\\r");
s = s.replaceAll("__NEWLINE_PLACEHOLDER__", newlineReplacement);
write("\"" + s + "\"");
} catch (IOException e) {
getProblems().add(new EmbedUnableToReadSourceProblem(e, file.getPath()));
}
}
}
//--------------------------------------------------------------------------
// Specific
//--------------------------------------------------------------------------
public void emitIsAs(IExpressionNode node, IExpressionNode left, IExpressionNode right, ASTNodeID id, boolean coercion)
{
asIsEmitter.emitIsAs(node, left, right, id, coercion);
}
@Override
protected void emitSelfReference(IFunctionNode node)
{
selfReferenceEmitter.emit(node);
}
@Override
protected void emitObjectDefineProperty(IAccessorNode node)
{
objectDefinePropertyEmitter.emit(node);
}
@Override
public void emitDefinePropertyFunction(IAccessorNode node)
{
definePropertyFunctionEmitter.emit(node);
}
public String stringifyDefineProperties(IClassDefinition cdef)
{
setBufferWrite(true);
accessorEmitter.emit(cdef);
String result = getBuilder().toString();
getBuilder().setLength(0);
setBufferWrite(false);
return result;
}
@Override
public void emitClosureStart()
{
if (getDocEmitter() instanceof JSRoyaleDocEmitter && ((JSRoyaleDocEmitter) getDocEmitter()).getSuppressClosure()) return;
ICompilerProject project = getWalker().getProject();
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
write(JSRoyaleEmitterTokens.CLOSURE_FUNCTION_NAME);
write(ASEmitterTokens.PAREN_OPEN);
}
@Override
public void emitClosureEnd(IASNode node, IDefinition nodeDef)
{
if (getDocEmitter() instanceof JSRoyaleDocEmitter && ((JSRoyaleDocEmitter) getDocEmitter()).getSuppressClosure()) return;
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
write(ASEmitterTokens.SINGLE_QUOTE);
if (node.getNodeID() == ASTNodeID.IdentifierID)
{
if (nodeDef instanceof FunctionDefinition &&
isCustomNamespace((FunctionDefinition)nodeDef))
{
String ns = ((INamespaceResolvedReference)((FunctionDefinition)nodeDef).getNamespaceReference()).resolveAETNamespace(getWalker().getProject()).getName();
write(ns + "::");
}
write(nodeDef.getBaseName());
}
else if (node.getNodeID() == ASTNodeID.MemberAccessExpressionID)
writeChainName(node);
else
System.out.println("unexpected node in emitClosureEnd");
write(ASEmitterTokens.SINGLE_QUOTE);
write(ASEmitterTokens.PAREN_CLOSE);
}
@Override
public void emitStatement(IASNode node)
{
if (node instanceof IFunctionNode)
{
IFunctionNode fnode = (IFunctionNode) node;
if(fnode.getName().length() > 0)
{
// don't emit named local functions as statements, if they have
// a name. these functions are emitted as part of the function
// block header because that is required by JS strict mode.
// anonymous functions (functions without a name) are allowed.
return;
}
}
super.emitStatement(node);
}
private void writeChainName(IASNode node)
{
while (node.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
node = ((IMemberAccessExpressionNode)node).getRightOperandNode();
}
if (node.getNodeID() == ASTNodeID.IdentifierID)
write(((IdentifierNode)node).getName());
else
System.out.println("unexpected node in emitClosureEnd");
}
@Override
public void emitUnaryOperator(IUnaryOperatorNode node)
{
if (node.getNodeID() == ASTNodeID.Op_DeleteID)
{
if (node.getChild(0).getNodeID() == ASTNodeID.ArrayIndexExpressionID)
{
if (node.getChild(0).getChild(0).getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
MemberAccessExpressionNode obj = (MemberAccessExpressionNode)(node.getChild(0).getChild(0));
if (isXMLList(obj))
{
if (ASNodeUtils.hasParenOpen(node))
write(ASEmitterTokens.PAREN_OPEN);
getWalker().walk(obj);
DynamicAccessNode dan = (DynamicAccessNode)(node.getChild(0));
IASNode indexNode = dan.getChild(1);
write(".removeChildAt(");
getWalker().walk(indexNode);
write(")");
if (ASNodeUtils.hasParenClose(node))
write(ASEmitterTokens.PAREN_CLOSE);
return;
}
}
else if (node.getChild(0).getChild(0).getNodeID() == ASTNodeID.IdentifierID)
{
if (isXML((IdentifierNode)(node.getChild(0).getChild(0))))
{
if (ASNodeUtils.hasParenOpen(node))
write(ASEmitterTokens.PAREN_OPEN);
getWalker().walk(node.getChild(0).getChild(0));
DynamicAccessNode dan = (DynamicAccessNode)(node.getChild(0));
IASNode indexNode = dan.getChild(1);
write(".removeChild(");
getWalker().walk(indexNode);
write(")");
if (ASNodeUtils.hasParenClose(node))
write(ASEmitterTokens.PAREN_CLOSE);
return;
}
else if (isProxy((IdentifierNode)(node.getChild(0).getChild(0))))
{
if (ASNodeUtils.hasParenOpen(node))
write(ASEmitterTokens.PAREN_OPEN);
getWalker().walk(node.getChild(0).getChild(0));
DynamicAccessNode dan = (DynamicAccessNode)(node.getChild(0));
IASNode indexNode = dan.getChild(1);
write(".deleteProperty(");
getWalker().walk(indexNode);
write(")");
if (ASNodeUtils.hasParenClose(node))
write(ASEmitterTokens.PAREN_CLOSE);
return;
}
}
}
else if (node.getChild(0).getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
MemberAccessExpressionNode obj = (MemberAccessExpressionNode)(node.getChild(0));
if (isXMLList(obj))
{
if (ASNodeUtils.hasParenOpen(node))
write(ASEmitterTokens.PAREN_OPEN);
String s = stringifyNode(obj.getLeftOperandNode());
write(s);
write(".removeChild('");
s = stringifyNode(obj.getRightOperandNode());
write(s);
write("')");
if (ASNodeUtils.hasParenClose(node))
write(ASEmitterTokens.PAREN_CLOSE);
return;
}
}
}
else if (node.getNodeID() == ASTNodeID.Op_AtID)
{
IASNode op = node.getOperandNode();
if (op != null)
{
if (EmitterUtils.writeE4xFilterNode(getWalker().getProject(), getModel(), node))
write("node.");
write("attribute('");
getWalker().walk(node.getOperandNode());
write("')");
}
else if (node.getParent().getNodeID() == ASTNodeID.ArrayIndexExpressionID)
{
DynamicAccessNode parentNode = (DynamicAccessNode)node.getParent();
if (EmitterUtils.writeE4xFilterNode(getWalker().getProject(), getModel(), node))
write("node.");
write("attribute(");
getWalker().walk(parentNode.getRightOperandNode());
write(")");
}
return;
}
super.emitUnaryOperator(node);
}
/**
* resolveType on an XML expression returns null
* (see IdentiferNode.resolveType).
* So, we have to walk the tree ourselves and resolve
* individual pieces.
* We want to know not just whether the node is of type XML,
* but whether it is a property of a property of type XML.
* For example, this.foo might be XML or XMLList, but since
* 'this' isn't also XML, we return false. That's because
* assignment to this.foo shouldn't use setChild() but
* just do an assignment.
* @param obj
* @return
*/
public boolean isXMLList(MemberAccessExpressionNode obj)
{
IExpressionNode leftNode = obj.getLeftOperandNode();
IExpressionNode rightNode = obj.getRightOperandNode();
ASTNodeID rightID = rightNode.getNodeID();
if (rightID == ASTNodeID.IdentifierID)
{
IDefinition rightDef = rightNode.resolveType(getWalker().getProject());
if (rightDef != null)
{
if (SemanticUtils.isXMLish(rightDef, getWalker().getProject()))
{
return isLeftNodeXMLish(leftNode);
}
return false;
}
return isLeftNodeXMLish(leftNode);
}
else if (rightID == ASTNodeID.Op_AtID)
return true;
return false;
}
public boolean isLeftNodeXMLish(IExpressionNode leftNode)
{
ASTNodeID leftID = leftNode.getNodeID();
if (leftID == ASTNodeID.IdentifierID)
{
IDefinition leftDef = leftNode.resolveType(getWalker().getProject());
if (leftDef != null)
return SemanticUtils.isXMLish(leftDef, getWalker().getProject());
}
else if (leftID == ASTNodeID.MemberAccessExpressionID || leftID == ASTNodeID.Op_DescendantsID)
{
MemberAccessExpressionNode maen = (MemberAccessExpressionNode)leftNode;
IExpressionNode rightNode = maen.getRightOperandNode();
ASTNodeID rightID = rightNode.getNodeID();
if (rightID == ASTNodeID.IdentifierID)
{
IDefinition rightDef = rightNode.resolveType(getWalker().getProject());
if (rightDef != null)
{
return SemanticUtils.isXMLish(rightDef, getWalker().getProject());
}
}
leftNode = maen.getLeftOperandNode();
return isLeftNodeXMLish(leftNode);
}
else if (leftID == ASTNodeID.FunctionCallID)
{
FunctionCallNode fcn = (FunctionCallNode)leftNode;
String fname = fcn.getFunctionName();
if (fname.equals("XML") || fname.equals("XMLList"))
return true;
}
else if (leftID == ASTNodeID.Op_AsID)
{
BinaryOperatorAsNode boan = (BinaryOperatorAsNode)leftNode;
String fname = ((IdentifierNode)boan.getChild(1)).getName();
if (fname.equals("XML") || fname.equals("XMLList"))
return true;
}
else if (leftID == ASTNodeID.ArrayIndexExpressionID)
{
leftNode = (IExpressionNode)(leftNode.getChild(0));
IDefinition leftDef = leftNode.resolveType(getWalker().getProject());
if (leftDef != null)
return SemanticUtils.isXMLish(leftDef, getWalker().getProject());
}
else if (leftID == ASTNodeID.E4XFilterID)
return true;
return false;
}
/**
* resolveType on an XML expression returns null
* (see IdentiferNode.resolveType).
* So, we have to walk the tree ourselves and resolve
* individual pieces.
* @param obj
* @return
*/
public boolean isProxy(IExpressionNode obj)
{
RoyaleProject project = (RoyaleProject)getWalker().getProject();
// See if it is Proxy
ITypeDefinition leftDef = obj.resolveType(project);
if (leftDef == null)
{
if (obj.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
IExpressionNode leftNode = ((MemberAccessExpressionNode)obj).getLeftOperandNode();
leftDef = leftNode.resolveType(project);
if (leftDef != null && leftDef.isInstanceOf(project.getProxyBaseClass(), project))
return true;
while (leftNode.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
// walk up chain looking for a proxy
leftNode = ((MemberAccessExpressionNode)leftNode).getLeftOperandNode();
leftDef = leftNode.resolveType(project);
if (leftDef != null && leftDef.isInstanceOf(project.getProxyBaseClass(), project))
return true;
}
}
return false;
}
return leftDef.isInstanceOf(project.getProxyBaseClass(), project);
}
/**
* resolveType on an XML expression returns null
* (see IdentiferNode.resolveType).
* So, we have to walk the tree ourselves and resolve
* individual pieces.
* @param obj
* @return
*/
public boolean isDateProperty(IExpressionNode obj, boolean writeAccess)
{
RoyaleProject project = (RoyaleProject)getWalker().getProject();
if (obj.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
IDefinition leftDef;
IExpressionNode leftNode = ((MemberAccessExpressionNode)obj).getLeftOperandNode();
IExpressionNode rightNode = ((MemberAccessExpressionNode)obj).getRightOperandNode();
leftDef = leftNode.resolveType(project);
IDefinition rightDef = rightNode.resolve(project);
if (leftDef != null && leftDef.getQualifiedName().equals("Date"))
{
if (rightDef instanceof AccessorDefinition)
return true;
else if (rightDef instanceof VariableDefinition)
return true;
else if (rightDef == null && rightNode.getNodeID() == ASTNodeID.IdentifierID)
{
if (writeAccess)
{
DatePropertiesSetters propSetter = DatePropertiesSetters.valueOf(((IIdentifierNode)rightNode).getName().toUpperCase());
if (propSetter != null) return true;
}
else
{
DatePropertiesGetters propGetter = DatePropertiesGetters.valueOf(((IIdentifierNode)rightNode).getName().toUpperCase());
if (propGetter != null) return true;
}
}
}
}
return false;
}
/**
* resolveType on an XML expression returns null
* (see IdentiferNode.resolveType).
* So, we have to walk the tree ourselves and resolve
* individual pieces.
* @param obj
* @return
*/
public boolean isXML(IExpressionNode obj)
{
// See if the left side is XML or XMLList
IDefinition leftDef = obj.resolveType(getWalker().getProject());
if (leftDef == null && obj.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
return isXML(((MemberAccessExpressionNode)obj).getLeftOperandNode());
}
else if (leftDef != null && leftDef.getBaseName().equals("*") && obj instanceof DynamicAccessNode) {
return isXML(((DynamicAccessNode)obj).getLeftOperandNode());
}
return SemanticUtils.isXMLish(leftDef, getWalker().getProject());
}
public MemberAccessExpressionNode getLastMAEInChain(MemberAccessExpressionNode node)
{
while (node.getRightOperandNode() instanceof MemberAccessExpressionNode)
node = (MemberAccessExpressionNode)node.getRightOperandNode();
return node;
}
@Override
public void emitLabelStatement(LabeledStatementNode node)
{
BlockNode innerBlock = node.getLabeledStatement();
if (innerBlock.getChildCount() == 1 && innerBlock.getChild(0).getNodeID() == ASTNodeID.ForEachLoopID)
{
getWalker().walk(node.getLabeledStatement());
return; // for each emitter will emit label in the right spot
}
super.emitLabelStatement(node);
}
@Override
public void emitTypedExpression(ITypedExpressionNode node)
{
ICompilerProject project = getWalker().getProject();
if (project instanceof RoyaleJSProject)
{
String vectorClassName = ((RoyaleJSProject)project).config == null ? null : ((RoyaleJSProject)project).config.getJsVectorEmulationClass();
if (vectorClassName != null)
{
if (!vectorClassName.equals("Array"))
write(vectorClassName);
return;
}
}
Boolean written = false;
if (node instanceof TypedExpressionNode) {
startMapping(node);
write(ASEmitterTokens.PAREN_OPEN);
write(JSRoyaleEmitterTokens.SYNTH_VECTOR);
write(ASEmitterTokens.PAREN_OPEN);
write(ASEmitterTokens.SINGLE_QUOTE);
//the element type of the Vector:
write(formatQualifiedName(node.getTypeNode().resolve(project).getQualifiedName()));
write(ASEmitterTokens.SINGLE_QUOTE);
write(ASEmitterTokens.PAREN_CLOSE);
write(ASEmitterTokens.PAREN_CLOSE);
endMapping(node);
written = true;
}
if (!written) {
write(JSRoyaleEmitterTokens.VECTOR);
}
if (getModel().inStaticInitializer)
{
if (!staticUsedNames.contains(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken()))
staticUsedNames.add(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken());
}
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
}
boolean isGoogProvided(String className)
{
ICompilerProject project = getWalker().getProject();
return ((RoyaleJSProject)project).isGoogProvided(className);
}
}