blob: 0be6c74559645c2db3aa2983a13d67e1e96306dc [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.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.royale.compiler.constants.IASKeywordConstants;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType;
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.IFunctionDefinition;
import org.apache.royale.compiler.definitions.IFunctionDefinition.FunctionClassification;
import org.apache.royale.compiler.definitions.ITypeDefinition;
import org.apache.royale.compiler.definitions.IVariableDefinition;
import org.apache.royale.compiler.internal.codegen.js.JSSessionModel;
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.InterfaceDefinition;
import org.apache.royale.compiler.internal.definitions.NamespaceDefinition.INamepaceDeclarationDirective;
import org.apache.royale.compiler.internal.definitions.ParameterDefinition;
import org.apache.royale.compiler.internal.definitions.VariableDefinition;
import org.apache.royale.compiler.internal.semantics.SemanticUtils;
import org.apache.royale.compiler.internal.tree.as.*;
import org.apache.royale.compiler.internal.tree.as.ConfigConditionBlockNode;
import org.apache.royale.compiler.internal.tree.as.ContainerNode;
import org.apache.royale.compiler.internal.tree.as.NodeBase;
import org.apache.royale.compiler.internal.tree.as.ParameterNode;
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.NativeUtils;
/**
* Various static methods used in shared emitter logic.
*/
public class EmitterUtils
{
public static ITypeNode findTypeNode(IPackageNode node)
{
IScopedNode scope = node.getScopedNode();
for (int i = 0; i < scope.getChildCount(); i++)
{
IASNode child = scope.getChild(i);
if (child instanceof ITypeNode)
return (ITypeNode) child;
else if (child.getNodeID() == ASTNodeID.ConfigBlockID)
{
ConfigConditionBlockNode configNode = (ConfigConditionBlockNode)child;
if (configNode.getChildCount() > 0)
{
child = configNode.getChild(0);
if (child instanceof ITypeNode)
return (ITypeNode) child;
}
}
}
return null;
}
public static ITypeDefinition findType(Collection<IDefinition> definitions)
{
for (IDefinition definition : definitions)
{
if (definition instanceof ITypeDefinition)
return (ITypeDefinition) definition;
}
return null;
}
public static INamepaceDeclarationDirective findNamespace(Collection<IDefinition> definitions)
{
for (IDefinition definition : definitions)
{
if (definition instanceof INamepaceDeclarationDirective)
return (INamepaceDeclarationDirective) definition;
}
return null;
}
public static INamespaceNode findNamespaceNode(IPackageNode node)
{
IScopedNode scope = node.getScopedNode();
for (int i = 0; i < scope.getChildCount(); i++)
{
IASNode child = scope.getChild(i);
if (child instanceof INamespaceNode)
return (INamespaceNode) child;
}
return null;
}
public static IFunctionDefinition findFunction(Collection<IDefinition> definitions)
{
for (IDefinition definition : definitions)
{
if (definition instanceof IFunctionDefinition)
return (IFunctionDefinition) definition;
}
return null;
}
public static IFunctionNode findFunctionNode(IPackageNode node)
{
IScopedNode scope = node.getScopedNode();
for (int i = 0; i < scope.getChildCount(); i++)
{
IASNode child = scope.getChild(i);
if (child instanceof IFunctionNode)
return (IFunctionNode) child;
}
return null;
}
public static IVariableNode findVariableNode(IPackageNode node)
{
IScopedNode scope = node.getScopedNode();
for (int i = 0; i < scope.getChildCount(); i++)
{
IASNode child = scope.getChild(i);
if (child instanceof IVariableNode)
return (IVariableNode) child;
}
return null;
}
public static IVariableDefinition findVariable(Collection<IDefinition> definitions)
{
for (IDefinition definition : definitions)
{
if (definition instanceof IVariableDefinition)
return (IVariableDefinition) definition;
}
return null;
}
public static ITypeDefinition getTypeDefinition(IDefinitionNode node)
{
ITypeNode tnode = (ITypeNode) node.getAncestorOfType(ITypeNode.class);
if (tnode != null)
{
return (ITypeDefinition) tnode.getDefinition();
}
return null;
}
public static boolean isSameClass(IDefinition pdef, IDefinition thisClass,
ICompilerProject project)
{
if (pdef == thisClass)
return true;
IDefinition cdef = ((ClassDefinition) thisClass)
.resolveBaseClass(project);
while (cdef != null)
{
// needs to be a loop
if (cdef == pdef)
return true;
cdef = ((ClassDefinition) cdef).resolveBaseClass(project);
}
return false;
}
public static boolean hasSuperClass(ICompilerProject project,
IDefinitionNode node)
{
IClassDefinition superClassDefinition = getSuperClassDefinition(node,
project);
// XXX (mschmalle) this is nulling for MXML super class, figure out why
if (superClassDefinition == null)
return false;
String qname = superClassDefinition.getQualifiedName();
return superClassDefinition != null
&& !qname.equals(IASLanguageConstants.Object);
}
public static boolean hasSuperCall(IScopedNode node)
{
for (int i = node.getChildCount() - 1; i > -1; i--)
{
IASNode cnode = node.getChild(i);
if (cnode.getNodeID() == ASTNodeID.FunctionCallID
&& cnode.getChild(0).getNodeID() == ASTNodeID.SuperID)
return true;
}
return false;
}
public static boolean hasBody(IFunctionNode node)
{
IScopedNode scope = node.getScopedNode();
return scope.getChildCount() > 0;
}
public static IClassDefinition getSuperClassDefinition(
IDefinitionNode node, ICompilerProject project)
{
IDefinition parent = node.getDefinition().getParent();
if (parent instanceof IClassDefinition)
{
IClassDefinition parentClassDef = (IClassDefinition) parent;
IClassDefinition superClass = parentClassDef.resolveBaseClass(project);
return superClass;
}
return null;
}
public static List<String> resolveImports(ITypeDefinition type)
{
ArrayList<String> list = new ArrayList<String>();
IScopedNode scopeNode = type.getContainedScope().getScopeNode();
if (scopeNode != null)
{
scopeNode.getAllImports(list);
}
else
{
// MXML
ClassDefinition cdefinition = (ClassDefinition) type;
String[] implicitImports = cdefinition.getImplicitImports();
for (String imp : implicitImports)
{
list.add(imp);
}
}
return list;
}
public static IClassDefinition getClassDefinition(IDefinitionNode node)
{
IClassNode tnode = (IClassNode) node
.getAncestorOfType(IClassNode.class);
return (tnode != null) ? tnode.getDefinition() : null;
}
public static IParameterNode getRest(IParameterNode[] nodes)
{
for (IParameterNode node : nodes)
{
if (node.isRest())
return node;
}
return null;
}
public static Map<Integer, IParameterNode> getDefaults(
IParameterNode[] nodes)
{
Map<Integer, IParameterNode> result = new HashMap<Integer, IParameterNode>();
int i = 0;
boolean hasDefaults = false;
for (IParameterNode node : nodes)
{
if (node.hasDefaultValue())
{
hasDefaults = true;
result.put(i, node);
}
else
{
result.put(i, null);
}
i++;
}
if (!hasDefaults)
return null;
return result;
}
public static boolean writeThis(ICompilerProject project,
JSSessionModel model, IIdentifierNode node)
{
IClassNode classNode = (IClassNode) node
.getAncestorOfType(IClassNode.class);
IDefinition nodeDef = node.resolve(project);
IASNode parentNode = node.getParent();
ASTNodeID parentNodeId = parentNode.getNodeID();
IASNode firstChild = parentNode.getChild(0);
final IClassDefinition thisClass = model.getCurrentClass();
boolean identifierIsMemberAccess = parentNodeId == ASTNodeID.MemberAccessExpressionID;
if (nodeDef instanceof ParameterDefinition)
return false;
if (nodeDef instanceof InterfaceDefinition)
return false;
if (nodeDef instanceof ClassDefinition)
return false;
if (classNode == null) // script in MXML and AS interface definitions
{
if (parentNodeId == ASTNodeID.FunctionCallID && model.inE4xFilter)
{
// instance methods must be qualified with 'this'?
// or maybe we need to test if identifier exists on XML/XMLList
return false;
}
if (nodeDef instanceof VariableDefinition)
{
IDefinition pdef = ((VariableDefinition) nodeDef).getParent();
if (thisClass == null || !isSameClass(pdef, thisClass, project))
return false;
if (identifierIsMemberAccess)
return node == firstChild;
return parentNodeId == ASTNodeID.ContainerID
|| !(parentNode instanceof ParameterNode);
}
else if (nodeDef instanceof AccessorDefinition)
{
IDefinition pdef = ((AccessorDefinition) nodeDef).getParent();
if (thisClass == null || !isSameClass(pdef, thisClass, project))
return false;
if (identifierIsMemberAccess)
return node == firstChild;
return true;
}
else if (parentNodeId == ASTNodeID.ContainerID
&& nodeDef instanceof FunctionDefinition)
{
return ((FunctionDefinition) nodeDef)
.getFunctionClassification() == FunctionClassification.CLASS_MEMBER; // for 'goog.bind'
}
else
{
boolean isFileOrPackageMember = false;
boolean isLocalFunction = false;
if(nodeDef instanceof FunctionDefinition)
{
FunctionClassification classification = ((FunctionDefinition) nodeDef).getFunctionClassification();
if(classification == FunctionClassification.FILE_MEMBER ||
classification == FunctionClassification.PACKAGE_MEMBER)
{
isFileOrPackageMember = true;
}
else if (!identifierIsMemberAccess && classification == FunctionClassification.CLASS_MEMBER &&
isClassMember(project, nodeDef, thisClass))
{
return true;
}
else if (classification == FunctionClassification.LOCAL)
{
isLocalFunction = true;
}
}
return parentNodeId == ASTNodeID.FunctionCallID
&& !(nodeDef instanceof AccessorDefinition)
&& !identifierIsMemberAccess
&& !isFileOrPackageMember
&& !isLocalFunction;
}
}
else
{
if (parentNodeId == ASTNodeID.FunctionCallID && model.inE4xFilter)
{
// instance methods must be qualified with 'this'?
// or maybe we need to test if identifier exists on XML/XMLList
return false;
}
if (nodeDef != null
&& isClassMember(project, nodeDef, classNode))
{
if (identifierIsMemberAccess)
{
return node == firstChild;
}
else
{
boolean identifierIsLocalFunction = nodeDef instanceof FunctionDefinition
&& !(nodeDef instanceof AccessorDefinition)
&& ((FunctionDefinition) nodeDef)
.getFunctionClassification() == IFunctionDefinition.FunctionClassification.LOCAL;
return !identifierIsLocalFunction;
}
}
}
return false;
}
public static boolean isClassMember(ICompilerProject project,
IDefinition nodeDef, IClassNode classNode)
{
IDefinition parentDef = nodeDef.getParent();
if (nodeDef.isInternal() && (!(parentDef instanceof ClassDefinition)))
return false;
IClassDefinition cdef = classNode.getDefinition();
return parentDef == cdef || (parentDef instanceof ClassDefinition && cdef.isInstanceOf((ClassDefinition)parentDef, project));
}
public static boolean isClassMember(ICompilerProject project,
IDefinition nodeDef, IClassDefinition classDef)
{
IDefinition parentDef = nodeDef.getParent();
if (nodeDef.isInternal() && (!(parentDef instanceof ClassDefinition)))
return false;
return parentDef == classDef || (parentDef instanceof ClassDefinition && ((ClassDefinition)parentDef).isInstanceOf(classDef, project));
}
public static boolean writeE4xFilterNode(ICompilerProject project,
JSSessionModel model, IExpressionNode node)
{
if (!model.inE4xFilter) return false;
IDefinition nodeDef = node.resolve(project);
IASNode parentNode = node.getParent();
// ASTNodeID parentNodeId = parentNode.getNodeID();
IASNode firstChild = parentNode.getChild(0);
// final IClassDefinition thisClass = model.getCurrentClass();
// boolean identifierIsMemberAccess = parentNodeId == ASTNodeID.MemberAccessExpressionID;
if (parentNode instanceof IUnaryOperatorNode)
return false;
if (nodeDef instanceof ParameterDefinition)
return false;
if (nodeDef instanceof InterfaceDefinition)
return false;
if (nodeDef instanceof ClassDefinition)
return false;
if (nodeDef instanceof VariableDefinition)
{
List<IVariableNode> list = model.getVars();
for (IVariableNode element : list) {
if(element.getQualifiedName().equals(((IIdentifierNode)node).getName()))
return false;
}
}
if (node == firstChild)
return true;
//support alternate ordering of checks, e.g. listItem.(3 == @id) as well as the normal listItem.(@id == 3)
if (parentNode instanceof IBinaryOperatorNode
&& node == parentNode.getChild(1)) {
return !(parentNode instanceof IMemberAccessExpressionNode);
}
return false;
}
/* public static ArrayList<String> amendE4XFilterComparison(ICompilerProject project,
JSSessionModel model, IBinaryOperatorNode node, boolean isLeft) {
if (!model.inE4xFilter) return null;
IExpressionNode leftNode = node.getLeftOperandNode();
IDefinition left = null;
IDefinition right = null;
boolean leftNodeMember = false;
boolean leftNodeQName = false;
boolean rightNodeMember = false;
boolean rightNodeQName = false;
if (writeE4xFilterNode(project, model, leftNode)) {
leftNodeMember = true;
if (leftNode instanceof IFunctionCallNode &&
((IFunctionCallNode) leftNode).getNameNode() != null){
leftNodeQName = ((IFunctionCallNode) leftNode).getNameNode().equals("name");
}
} else {
left = node.getLeftOperandNode().resolveType(project);
}
IExpressionNode rightNode = node.getRightOperandNode();
if (writeE4xFilterNode(project, model, rightNode)) {
rightNodeMember = true;
if (rightNode instanceof IFunctionCallNode &&
((IFunctionCallNode) rightNode).getNameNode() != null){
rightNodeQName = ((IFunctionCallNode) rightNode).getNameNode().equals("name");
}
} else {
right = node.getRightOperandNode().resolveType(project);
}
if (!leftNodeMember && !rightNodeMember) return null;
ArrayList<String> s = null;
if (leftNodeMember) {
//left side is a node.something() function
if (right!= null && leftNodeQName) {
//node.name() == right
if (right.getQualifiedName().equals("String")) {
if (isLeft) {
s=new ArrayList<String>();
s.add(".toString()");
} //else make no change for right side
} else if (rightNodeQName || right.getQualifiedName().equals("QName")) {
//use qname.equals(otherQName)
if (isLeft) {
s.add(".equals(");
} else {
s.add(")"); //close the equals
}
}
}
}
if (s == null && rightNodeMember) {
//right side is a node.something() function that has not already been addressed
if (left!= null && rightNodeQName) {
//left == node.name()
if (left.getQualifiedName().equals("String")) {
if (!isLeft) {
//node.name().toString()
s=new ArrayList<String>();
s.add(".toString()");
} //else make no change for right side
} else if (rightNodeQName || right.getQualifiedName().equals("QName")) {
//use qname.equals(otherQName)
if (isLeft) {
s.add(".equals(");
} else {
s.add(")"); //close the equals
}
}
}
}
return s;
}*/
public static boolean isScalar(IExpressionNode node)
{
ASTNodeID id = node.getNodeID();
if (id == ASTNodeID.LiteralBooleanID ||
id == ASTNodeID.LiteralIntegerID ||
id == ASTNodeID.LiteralIntegerZeroID ||
id == ASTNodeID.LiteralDoubleID ||
id == ASTNodeID.LiteralNullID ||
id == ASTNodeID.LiteralNumberID ||
id == ASTNodeID.LiteralRegexID ||
id == ASTNodeID.LiteralStringID ||
id == ASTNodeID.LiteralUintID)
return true;
if (id == ASTNodeID.IdentifierID)
{
IIdentifierNode idnode = (IIdentifierNode)node;
String idname = idnode.getName();
if (idname.equals(NativeUtils.NativeASType.Infinity.name()) ||
idname.equals(NativeUtils.NativeASType.undefined.name()) ||
idname.equals(NativeUtils.NativeASType.NaN.name()))
return true;
}
// special case -Infinity
if (id == ASTNodeID.Op_SubtractID &&
node.getChildCount() == 1)
{
IASNode child = node.getChild(0);
if (child.getNodeID() == ASTNodeID.IdentifierID)
{
IIdentifierNode idnode = (IIdentifierNode)child;
String idname = idnode.getName();
if (idname.equals(NativeUtils.NativeASType.Infinity.name()))
return true;
}
}
return false;
}
// return true if the node is an expression that may not work
// as the initial value of a static var at
// static initialization time. Such as a function call to
// another static method in the class.
// Non-static initializers have different rules: even simple object
// and arrays need to be created for each instance, but for statics
// simple objects and arras are ok.
public static boolean needsStaticInitializer(String node, String className)
{
return node.contains(className);
}
public static IContainerNode insertArgumentsBefore(IContainerNode argumentsNode, IASNode... nodes)
{
int originalLength = argumentsNode.getChildCount();
int extraLength = nodes.length;
ContainerNode result = new ContainerNode(originalLength + extraLength);
result.setSourcePath(argumentsNode.getSourcePath());
result.span(argumentsNode);
result.setParent((NodeBase) argumentsNode.getParent());
for (int i = 0; i < extraLength; i++)
{
NodeBase node = (NodeBase) nodes[i];
node.setSourcePath(argumentsNode.getSourcePath());
result.addItem(node);
}
for (int i = 0; i < originalLength; i++)
{
result.addItem((NodeBase) argumentsNode.getChild(i));
}
return result;
}
public static IContainerNode insertArgumentsAfter(IContainerNode argumentsNode, IASNode... nodes)
{
int originalLength = argumentsNode.getChildCount();
int extraLength = nodes.length;
ContainerNode result = new ContainerNode(originalLength + extraLength);
result.setSourcePath(argumentsNode.getSourcePath());
result.span(argumentsNode);
result.setParent((NodeBase) argumentsNode.getParent());
for (int i = 0; i < originalLength; i++)
{
result.addItem((NodeBase) argumentsNode.getChild(i));
}
for (int i = 0; i < extraLength; i++)
{
NodeBase node = (NodeBase) nodes[i];
node.setSourcePath(argumentsNode.getSourcePath());
result.addItem(node);
}
return result;
}
public static IContainerNode insertArgumentsAt(IContainerNode argumentsNode, int index, IASNode... nodes)
{
int originalLength = argumentsNode.getChildCount();
int extraLength = nodes.length;
ContainerNode result = new ContainerNode(originalLength + extraLength);
result.setSourcePath(argumentsNode.getSourcePath());
result.span(argumentsNode);
result.setParent((NodeBase) argumentsNode.getParent());
for (int i = 0; i < originalLength; i++)
{
if(i < index)
{
result.addItem((NodeBase) argumentsNode.getChild(i));
}
else
{
if (i == index)
{
for (IASNode node : nodes)
{
NodeBase n = (NodeBase) node;
n.setSourcePath(argumentsNode.getSourcePath());
result.addItem(n);
}
}
result.addItem((NodeBase) argumentsNode.getChild(i));
}
}
return result;
}
public static boolean isImplicit(IContainerNode node)
{
return node.getContainerType() == IContainerNode.ContainerType.IMPLICIT
|| node.getContainerType() == IContainerNode.ContainerType.SYNTHESIZED;
}
public static boolean needsDefaultValue(IVariableNode node, boolean defaultInitializers, ICompilerProject project)
{
if (node == null)
{
return false;
}
if (node instanceof IParameterNode)
{
return false;
}
if (node instanceof IAccessorNode)
{
return false;
}
IExpressionNode assignedValueNode = node.getAssignedValueNode();
if (assignedValueNode != null)
{
//already has an assigned value, so it doesn't need to be
//hoisted
return false;
}
IASNode parentNode = node.getParent();
if (parentNode instanceof IVariableExpressionNode)
{
//ignore for-in loops
return false;
}
IExpressionNode variableTypeNode = node.getVariableTypeNode();
if (variableTypeNode == null)
{
return false;
}
IDefinition varTypeDef = variableTypeNode.resolve(project);
if (varTypeDef == null)
{
return false;
}
if (IASLanguageConstants.ANY_TYPE.equals(varTypeDef.getQualifiedName()))
{
return false;
}
if (project.getBuiltinType(BuiltinType.ANY_TYPE).equals(varTypeDef)
|| project.getBuiltinType(BuiltinType.ANY_TYPE).equals(varTypeDef)
|| project.getBuiltinType(BuiltinType.ANY_TYPE).equals(varTypeDef))
{
return false;
}
if (project.getBuiltinType(BuiltinType.INT).equals(varTypeDef)
|| project.getBuiltinType(BuiltinType.UINT).equals(varTypeDef))
{
//always true, regardless of -js-default-initializers
return true;
}
return defaultInitializers;
}
/**
* 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 static boolean isXMLish(IExpressionNode obj, ICompilerProject project )
{
// See if the left side is XML or XMLList
IDefinition leftDef = obj.resolveType(project);
if (leftDef == null && obj.getNodeID() == ASTNodeID.MemberAccessExpressionID)
{
return isXMLish(((MemberAccessExpressionNode)obj).getLeftOperandNode(), project);
}
else if (leftDef != null && leftDef.getBaseName().equals("*") && obj instanceof DynamicAccessNode) {
return isXMLish(((DynamicAccessNode)obj).getLeftOperandNode(), project);
}
return SemanticUtils.isXMLish(leftDef, project);
}
/**
* 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 static boolean isXMLList(IMemberAccessExpressionNode obj, ICompilerProject project)
{
IExpressionNode leftNode = obj.getLeftOperandNode();
IExpressionNode rightNode = obj.getRightOperandNode();
ASTNodeID rightID = rightNode.getNodeID();
if (rightID == ASTNodeID.IdentifierID || (rightID == ASTNodeID.NamespaceAccessExpressionID && rightNode.getChild(1).getNodeID() == ASTNodeID.IdentifierID))
{
IDefinition rightDef = rightNode.resolveType(project);
if (rightDef != null)
{
if (SemanticUtils.isXMLish(rightDef, project))
{
return isLeftNodeXMLish(leftNode, project);
}
return false;
}
return isLeftNodeXMLish(leftNode, project);
}
else if (rightID == ASTNodeID.Op_AtID)
return true;
else if (rightNode instanceof IDynamicAccessNode && ((IDynamicAccessNode) rightNode).getLeftOperandNode().getNodeID() == ASTNodeID.Op_AtID)
return true;
return false;
}
public static boolean isLeftNodeXMLList(IExpressionNode leftNode, ICompilerProject project) {
boolean isXMLList = false;
if (isLeftNodeXMLish(leftNode, project)) {
//it is not XMLList if it is a DynamicAccessNode with numeric index.
//this is limited analysis, because ["0"] would also be the same as [0], but perhaps best we can do without more runtime support
if (leftNode instanceof IDynamicAccessNode) { //DynamicAccessNode
IExpressionNode dynAccess = ((IDynamicAccessNode) leftNode).getRightOperandNode();
IDefinition accessDef = dynAccess.resolveType(project);
if (SemanticUtils.isNumericType(accessDef, project)) {
//assume we are XML, not XMLList
isXMLList = false;
}
} else
isXMLList = true;
}
return isXMLList;
}
public static boolean isLeftNodeXML(IExpressionNode leftNode, ICompilerProject project) {
boolean isXML = false;
if (isLeftNodeXMLish(leftNode, project)) {
//it is not XMLList if it is a DynamicAccessNode with numeric index.
//this is limited analysis, because ["0"] would also be the same as [0], but perhaps best we can do without more runtime support
if (leftNode instanceof IDynamicAccessNode) { //DynamicAccessNode
IExpressionNode dynAccess = ((IDynamicAccessNode) leftNode).getRightOperandNode();
IDefinition accessDef = dynAccess.resolveType(project);
if (SemanticUtils.isNumericType(accessDef, project)) {
//assume we are XML, not XMLList
isXML = true;
}
} else
isXML = false;
}
return isXML;
}
public static boolean isLeftNodeXMLish(IExpressionNode leftNode, ICompilerProject project)
{
ASTNodeID leftID = leftNode.getNodeID();
if (leftID == ASTNodeID.IdentifierID)
{
IDefinition leftDef = leftNode.resolveType(project);
if (leftDef != null)
return SemanticUtils.isXMLish(leftDef, project);
}
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(project);
if (rightDef != null && rightDef != project.getBuiltinType(BuiltinType.ANY_TYPE))
{
return SemanticUtils.isXMLish(rightDef, project);
}
}
leftNode = maen.getLeftOperandNode();
return isLeftNodeXMLish(leftNode, project);
}
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(project);
if (leftDef != null)
return SemanticUtils.isXMLish(leftDef, project);
}
else if (leftID == ASTNodeID.E4XFilterID)
return true;
return false;
}
public static boolean needsXMLQNameArgumentsPatch(IFunctionCallNode node, ICompilerProject project) {
if (node.getNameNode() instanceof MemberAccessExpressionNode
&& ((MemberAccessExpressionNode)node.getNameNode()).getRightOperandNode() instanceof IdentifierNode) {
String methodName = ((IdentifierNode)((MemberAccessExpressionNode)node.getNameNode()).getRightOperandNode()).getName();
if ("child".equals(methodName) || "descendants".equals(methodName) || "attribute".equals(methodName)) {
//double check it is not a method with the same name on a non-XMLish class
IASNode leftNode = ((MemberAccessExpressionNode)node.getNameNode()).getLeftOperandNode();
boolean isXML = leftNode instanceof MemberAccessExpressionNode
&& isLeftNodeXMLish((MemberAccessExpressionNode) leftNode, project);
if (!isXML) {
isXML = leftNode instanceof IExpressionNode && isXMLish((IExpressionNode)leftNode, project);
}
if (isXML) {
//check argumentsNode
if (node.getArgumentsNode().getChildCount() == 1) {
IDefinition def = node.getArgumentNodes()[0].resolveType(project);
if (def != null && def.getQualifiedName().equals("QName")) {
return true;
}
}
}
}
}
return false;
}
public static void createDefaultNamespaceArg(ContainerNode argsNode, int position, IExpressionNode defaultNamespace) {
argsNode.addChild((NodeBase) defaultNamespace, position);
((NodeBase) defaultNamespace).setParent(argsNode);
}
public static boolean isCustomNamespace(String ns) {
if (ns != null)
{
return (!( ns.equals(IASKeywordConstants.PRIVATE) ||
ns.equals(IASKeywordConstants.PROTECTED) ||
ns.equals(IASKeywordConstants.INTERNAL) ||
ns.equals(INamespaceConstants.AS3URI) ||
ns.equals(IASKeywordConstants.PUBLIC)
));
}
return false;
}
}