/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.internal.codegen.js.utils; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.IFunctionDefinition; import org.apache.flex.compiler.definitions.IFunctionDefinition.FunctionClassification; import org.apache.flex.compiler.definitions.INamespaceDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.IVariableDefinition; import org.apache.flex.compiler.internal.codegen.js.JSSessionModel; import org.apache.flex.compiler.internal.definitions.AccessorDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.FunctionDefinition; import org.apache.flex.compiler.internal.definitions.InterfaceDefinition; import org.apache.flex.compiler.internal.definitions.NamespaceDefinition.INamepaceDeclarationDirective; import org.apache.flex.compiler.internal.definitions.ParameterDefinition; import org.apache.flex.compiler.internal.definitions.VariableDefinition; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.scopes.TypeScope; import org.apache.flex.compiler.internal.tree.as.ContainerNode; import org.apache.flex.compiler.internal.tree.as.NodeBase; import org.apache.flex.compiler.internal.tree.as.ParameterNode; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.tree.ASTNodeID; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IClassNode; import org.apache.flex.compiler.tree.as.IContainerNode; import org.apache.flex.compiler.tree.as.IDefinitionNode; import org.apache.flex.compiler.tree.as.IExpressionNode; import org.apache.flex.compiler.tree.as.IFunctionNode; import org.apache.flex.compiler.tree.as.IIdentifierNode; import org.apache.flex.compiler.tree.as.INamespaceNode; import org.apache.flex.compiler.tree.as.IPackageNode; import org.apache.flex.compiler.tree.as.IParameterNode; import org.apache.flex.compiler.tree.as.IScopedNode; import org.apache.flex.compiler.tree.as.ITypeNode; import org.apache.flex.compiler.tree.as.IVariableNode; import org.apache.flex.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; } 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; 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; } return parentNodeId == ASTNodeID.FunctionCallID && !(nodeDef instanceof AccessorDefinition) && !identifierIsMemberAccess && !isFileOrPackageMember; } } 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) { if (nodeDef.isInternal() && (!(nodeDef.getParent() instanceof ClassDefinition))) return false; TypeScope cscope = (TypeScope) classNode.getDefinition() .getContainedScope(); Set<INamespaceDefinition> nsSet = cscope.getNamespaceSet(project); Collection<IDefinition> defs = new HashSet<IDefinition>(); cscope.getAllPropertiesForMemberAccess((CompilerProject) project, defs, nsSet); Iterator<IDefinition> visiblePropertiesIterator = defs.iterator(); while (visiblePropertiesIterator.hasNext()) { if (nodeDef.getQualifiedName().equals( visiblePropertiesIterator.next().getQualifiedName())) return true; } return false; } public static boolean isClassMember(ICompilerProject project, IDefinition nodeDef, IClassDefinition classDef) { if (nodeDef.isInternal() && (!(nodeDef.getParent() instanceof ClassDefinition))) return false; TypeScope cscope = (TypeScope) classDef .getContainedScope(); Set<INamespaceDefinition> nsSet = cscope.getNamespaceSet(project); Collection<IDefinition> defs = new HashSet<IDefinition>(); cscope.getAllPropertiesForMemberAccess((CompilerProject) project, defs, nsSet); Iterator<IDefinition> visiblePropertiesIterator = defs.iterator(); while (visiblePropertiesIterator.hasNext()) { if (nodeDef.getQualifiedName().equals( visiblePropertiesIterator.next().getQualifiedName())) return true; } return false; } 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 < originalLength) { result.addItem((NodeBase) argumentsNode.getChild(i)); } else { int j = i; if (i >= index + extraLength) { j -= extraLength; result.addItem((NodeBase) argumentsNode.getChild(j)); } else { j -= originalLength; NodeBase node = (NodeBase) nodes[j]; node.setSourcePath(argumentsNode.getSourcePath()); result.addItem(node); } } } return result; } public static boolean isImplicit(IContainerNode node) { return node.getContainerType() == IContainerNode.ContainerType.IMPLICIT || node.getContainerType() == IContainerNode.ContainerType.SYNTHESIZED; } }