blob: 8906935f99e467ad5d3d565ee9b8cae0d2a62e4b [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.IParameterDefinition;
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.ClassDefinition;
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.scopes.FunctionScope;
import org.apache.royale.compiler.internal.scopes.PackageScope;
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.*;
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 String formatGetter(String name) {
String prefix = JSRoyaleEmitterTokens.GETTER_PREFIX.getToken();
RoyaleJSProject project = (RoyaleJSProject) getWalker().getProject();
if (project.config != null)
{
String configPrefix = project.config.getJsGetterPrefix();
if(configPrefix != null && configPrefix.length() > 0)
{
prefix = configPrefix;
}
}
return prefix + name;
}
public String formatSetter(String name) {
String prefix = JSRoyaleEmitterTokens.SETTER_PREFIX.getToken();
RoyaleJSProject project = (RoyaleJSProject) getWalker().getProject();
if (project.config != null)
{
String configPrefix = project.config.getJsSetterPrefix();
if(configPrefix != null && configPrefix.length() > 0)
{
prefix = configPrefix;
}
}
return prefix + name;
}
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;
if (node.getContainingScope().getScope() instanceof PackageScope) {
startMapping(node);
write(formatQualifiedName(node.getQualifiedName()));
endMapping(node);
} else if (node.getContainingScope().getScope() instanceof FunctionScope){
writeToken(ASEmitterTokens.VAR);
write("/** @type {"+IASLanguageConstants.Namespace+"} */");
startMapping(node);
write(node.getName());
endMapping(node);
} else {
//class member - static scope
if (!(node.getDefinition().getParent() instanceof IClassDefinition)) {
startMapping(node);
write(node.getQualifiedName());
endMapping(node);
} else {
write(node.getDefinition().getParent().getQualifiedName());
write(ASEmitterTokens.MEMBER_ACCESS);
startMapping(node);
write(node.getName());
endMapping(node);
}
}
write(ASEmitterTokens.SPACE);
writeToken(ASEmitterTokens.EQUAL);
writeToken(ASEmitterTokens.NEW);
startMapping(node);
write(IASLanguageConstants.Namespace);
endMapping(node);
write(ASEmitterTokens.PAREN_OPEN);
if (!staticUsedNames.contains(IASLanguageConstants.Namespace))
staticUsedNames.add(IASLanguageConstants.Namespace);
IExpressionNode uriNode = node.getNamespaceURINode();
if (uriNode == null)
{
startMapping(node);
write(ASEmitterTokens.SINGLE_QUOTE);
write(node.getName());
write(ASEmitterTokens.SINGLE_QUOTE);
endMapping(node);
}
else
getWalker().walk(uriNode);
write(ASEmitterTokens.PAREN_CLOSE);
}
@Override
public void emitUseNamespace(IUseNamespaceNode node)
{
write("//use namespace ");
getWalker().walk(node.getTargetNamespaceNode());
}
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() == null || !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;
String s = stringifyNode(node.getRightOperandNode());
if (s.startsWith("(") && s.endsWith(")"))
s = s.substring(1, s.length() - 1);
if (s.contains("this")) {
//it *could* be in some string content, but most likely is an actual 'this' reference
//so always bind to 'this' to correctly resolve the external references for the instance scope of the original code.
write(".filter((function(/** @type {XML} */ node){return (");
write(s);
write(")})");
write(".bind(this))");
} else {
write(".filter(function(/** @type {XML} */ node){return (");
write(s);
write(")})");
}
getModel().inE4xFilter = false;
}
@Override
public void emitE4XDefaultNamespaceDirective(IDefaultXMLNamespaceNode node){
//leave a comment at the original code location
write("// e4x default namespace active: ");
getWalker().walk(node.getKeywordNode());
write(" = ");
getWalker().walk(node.getExpressionNode());
//register the namespace for the current function scope (and only for function scopes (observed behavior) )
if (node.getContainingScope().getScope() instanceof FunctionScope) {
getModel().registerDefaultXMLNamespace(((FunctionScope)node.getContainingScope().getScope()), node.getExpressionNode());
}
}
@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);
IIdentifierNode identifierNode = null;
if (node.getNodeID() == ASTNodeID.IdentifierID)
{
identifierNode = (IIdentifierNode) node;
}
else if (node.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
identifierNode = getChainedIdentifierNode(node);
}
if (identifierNode == null)
{
System.out.println("unexpected node in emitClosureEnd");
}
else
{
if (!node.equals(identifierNode))
{
nodeDef = identifierNode.resolve(getWalker().getProject());
}
if (nodeDef instanceof FunctionDefinition &&
isCustomNamespace((FunctionDefinition)nodeDef))
{
String ns = ((INamespaceResolvedReference)((FunctionDefinition)nodeDef).getNamespaceReference()).resolveAETNamespace(getWalker().getProject()).getName();
write(ns + "::");
}
String qname = nodeDef.getBaseName();
if (nodeDef != null && !nodeDef.isStatic() && (!(nodeDef instanceof IParameterDefinition)) && nodeDef.isPrivate() && getWalker().getProject().getAllowPrivateNameConflicts())
qname = formatPrivateName(nodeDef.getParent().getQualifiedName(), qname);
write(qname);
}
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 IIdentifierNode getChainedIdentifierNode(IASNode node)
{
while (node.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
node = ((IMemberAccessExpressionNode)node).getRightOperandNode();
}
if (node.getNodeID() == ASTNodeID.IdentifierID)
{
return (IIdentifierNode) node;
}
return null;
}
@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 (isXMLish((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);
IExpressionNode rightNode = obj.getRightOperandNode();
String s = stringifyNode(obj.getLeftOperandNode());
boolean avoidMethodAccess = (rightNode instanceof IDynamicAccessNode && ((IDynamicAccessNode) rightNode).getLeftOperandNode().getNodeID() == ASTNodeID.Op_AtID);
boolean needsQuotes = rightNode.getNodeID() != ASTNodeID.Op_AtID
&& !avoidMethodAccess;
write(s);
write(".removeChild(");
if (needsQuotes)
write("'");
else
if (!avoidMethodAccess) write(s + ".");
s = stringifyNode(rightNode);
write(s);
if (needsQuotes)
write("'");
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(");
if (op instanceof INamespaceAccessExpressionNode) {
//parent.@ns::attributeName
write("XML.createAttributeQName(");
write("new QName(");
getWalker().walk(((INamespaceAccessExpressionNode) op).getLeftOperandNode());
write(",'");
getWalker().walk(((INamespaceAccessExpressionNode) op).getRightOperandNode());
write("')");
write(")");
} else {
if (getModel().defaultXMLNamespaceActive
&& op instanceof IIdentifierNode
&& node.getContainingScope().getScope() instanceof FunctionScope
&& getModel().getDefaultXMLNamespace((FunctionScope)(node.getContainingScope().getScope())) !=null) {
//apply default namespace to this attributes query
//parent.@attributeName (with default xml namespace set in scope)
write("XML.swfCompatibleQuery(");
write("new QName(");
getWalker().walk(getModel().getDefaultXMLNamespace((FunctionScope)(node.getContainingScope().getScope())));
write(",'");
getWalker().walk(node.getOperandNode());
write("')");
write(", true"); //attribute flag
write(")");
} else {
//default namespace is ''
write("'");
getWalker().walk(node.getOperandNode());
write("'");
}
}
write(")");
}
else if (node.getParent().getNodeID() == ASTNodeID.ArrayIndexExpressionID)
{
DynamicAccessNode parentNode = (DynamicAccessNode)node.getParent();
if (EmitterUtils.writeE4xFilterNode(getWalker().getProject(), getModel(), node))
write("node.");
if (node.getParent().getParent() != null
&& node.getParent().getParent().getParent() != null
&& node.getParent().getParent().getParent().getNodeID() == ASTNodeID.Op_DeleteID) {
write ("'@' + ");
//example : delete myXML.@[MyConst];
//output myXML.removeChild('@' + MyConst) <-- variant is handled in emulation class
getWalker().walk(parentNode.getRightOperandNode());
return;
}
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(IMemberAccessExpressionNode obj)
{
return EmitterUtils.isXMLList(obj, getWalker().getProject());
}
public boolean isLeftNodeXMLish(IExpressionNode leftNode)
{
return EmitterUtils.isLeftNodeXMLish(leftNode, getWalker().getProject());
}
/**
* 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 isXMLish(IExpressionNode obj)
{
return EmitterUtils.isXMLish(obj, 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;
}
@Override
public void emitAssignmentCoercion(IExpressionNode assignedNode, IDefinition definition)
{
super.emitAssignmentCoercion(assignedNode, definition);
if (getModel().inStaticInitializer)
{
if (!staticUsedNames.contains(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken()))
staticUsedNames.add(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken());
}
}
boolean isGoogProvided(String className)
{
ICompilerProject project = getWalker().getProject();
return ((RoyaleJSProject)project).isGoogProvided(className);
}
public void processBindableSupport(IClassDefinition type,ArrayList<String> requiresList) {
ICompilerProject royaleProject = getWalker().getProject();
boolean needsBindableSupport = type.needsEventDispatcher(royaleProject);
if (needsBindableSupport) {
IClassDefinition bindableClassDef = type;
ClassDefinition objectClassDefinition = (ClassDefinition)royaleProject.getBuiltinType(
IASLanguageConstants.BuiltinType.OBJECT);
if (bindableClassDef.resolveBaseClass(royaleProject).equals(objectClassDefinition)) {
//keep the decision in the model for later
getModel().registerImplicitBindableImplementation( bindableClassDef,
ImplicitBindableImplementation.EXTENDS);
// add the requiresList support for extending the dispatcher class
if (!requiresList.contains(formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME))) {
requiresList.add(formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME));
}
} else {
//keep the decision in the model for later
getModel().registerImplicitBindableImplementation( bindableClassDef,
ImplicitBindableImplementation.IMPLEMENTS);
//add the requiresList support for implementing IEventDispatcher
if (!requiresList.contains(formatQualifiedName(BindableEmitter.DISPATCHER_INTERFACE_QNAME))) {
requiresList.add(formatQualifiedName(BindableEmitter.DISPATCHER_INTERFACE_QNAME));
}
if (!requiresList.contains(formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME))) {
requiresList.add(formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME));
}
}
}
if (!needsBindableSupport) {
//we still need to check for static-only bindable requirements. If it was also instance-bindable,
//then the static-only requirements have already been met above
needsBindableSupport = ((IClassDefinition) type).needsStaticEventDispatcher(royaleProject);
//static-only bindable *only* requires the Dispatcher class, not the interface
if (needsBindableSupport
&& !requiresList.contains(formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME))) {
requiresList.add(formatQualifiedName(BindableEmitter.DISPATCHER_CLASS_QNAME));
}
}
}
}