/* * * 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.tree.as; import java.util.ArrayList; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.constants.IASLanguageConstants.BuiltinType; import org.apache.flex.compiler.definitions.IAccessorDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.IFunctionDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.parsing.IASToken; 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.IExpressionNode; import org.apache.flex.compiler.tree.as.IFunctionCallNode; import org.apache.flex.compiler.tree.as.IIdentifierNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind; /** * ActionScript parse tree node representing a function call (e.g. myFunction(), * new Object()) */ public class FunctionCallNode extends ExpressionNodeBase implements IFunctionCallNode { /** * Constructor. */ public FunctionCallNode(IASToken keywordNew, ExpressionNodeBase nameNode) { assert keywordNew != null : "Expected 'new' token."; assert nameNode != null : "Expected name expression."; newKeywordNode = new KeywordNode(keywordNew); this.nameNode = nameNode; argumentsNode = new ContainerNode(2); } /** * Constructor. */ public FunctionCallNode(ExpressionNodeBase nameNode) { newKeywordNode = null; this.nameNode = nameNode; argumentsNode = new ContainerNode(2); setSourceLocation(nameNode); } /** * Copy constructor. * * @param other The node to copy. */ protected FunctionCallNode(FunctionCallNode other) { super(other); this.newKeywordNode = other.newKeywordNode != null ? new KeywordNode(other.newKeywordNode) : null; this.nameNode = other.nameNode != null ? other.nameNode.copy() : null; this.argumentsNode = new ContainerNode(other.argumentsNode.getChildCount()); for (IExpressionNode arg : other.getArgumentNodes()) { argumentsNode.addItem(((ExpressionNodeBase)arg).copy()); } } /** * The token that holds "new", if it is present */ private KeywordNode newKeywordNode; /** * The node that describes the function being called */ private ExpressionNodeBase nameNode; /** * The node that contains the arguments being passed */ private ContainerNode argumentsNode; // // NodeBase overrides // @Override public ASTNodeID getNodeID() { return ASTNodeID.FunctionCallID; } @Override public int getChildCount() { int count = 0; if (newKeywordNode != null) count++; if (nameNode != null) count++; if (argumentsNode != null) count++; return count; } @Override public IASNode getChild(int i) { if (i > getChildCount()) return null; if (i == 0) { if (newKeywordNode != null) return newKeywordNode; return nameNode != null ? nameNode : argumentsNode; } else if (i == 1) { return newKeywordNode != null ? nameNode : argumentsNode; } else if (i == 2) { return argumentsNode; } return null; } @Override protected void setChildren(boolean fillInOffsets) { if (newKeywordNode != null) newKeywordNode.setParent(this); if (nameNode != null) nameNode.setParent(this); if (argumentsNode != null) argumentsNode.setParent(this); } @Override protected void replaceChild(NodeBase child, NodeBase target) { if (child == nameNode && target instanceof ExpressionNodeBase) { nameNode = (ExpressionNodeBase)target; nameNode.setParent(this); } } /* * For debugging only. * Builds a string such as <code>"trace"</code> from the * name of the function being called. */ @Override protected boolean buildInnerString(StringBuilder sb) { String name = ""; ExpressionNodeBase nameNode = getNameNode(); if (nameNode instanceof IIdentifierNode) name = ((IIdentifierNode)nameNode).getName(); sb.append('"'); sb.append(name); sb.append('"'); return true; } // // ExpressionNodeBase overrides // @Override public ITypeDefinition resolveType(ICompilerProject project) { IDefinition calledFunction = this.nameNode.resolve(project); if (calledFunction instanceof IFunctionDefinition && // call of an accessor is just like call of a var !(calledFunction instanceof IAccessorDefinition)) { // new foo() returns the * type if (getNewKeywordNode() != null) return project.getBuiltinType(BuiltinType.ANY_TYPE); else return ((IFunctionDefinition)calledFunction).resolveReturnType(project); } else if (calledFunction instanceof ITypeDefinition) { // The Date(...) function returns a String. if (!isNewExpression() && calledFunction.getQualifiedName().equals(IASLanguageConstants.Date)) return project.getBuiltinType(BuiltinType.STRING); // Call of a class or interface - a cast - so the Type of the expression is the called class // should only have 1 argument, but semantics will check that return (ITypeDefinition)calledFunction; } // If we are anything else (var, accessor, etc) // or unknown (didn't resolve), then the result of the // call is '*' as far as the compiler is concerned. return project.getBuiltinType(BuiltinType.ANY_TYPE); } @Override protected FunctionCallNode copy() { return new FunctionCallNode(this); } // // IFunctionCallNode implementations // @Override public boolean isNewExpression() { return newKeywordNode != null; } @Override public ExpressionNodeBase getNameNode() { return nameNode; } @Override public String getFunctionName() { return nameNode instanceof IIdentifierNode ? ((IIdentifierNode)nameNode).getName() : ""; } @Override public IDefinition resolveCalledExpression(ICompilerProject project) { IExpressionNode nameNode = getNameNode(); return nameNode.resolve(project); } @Override public IExpressionNode[] getArgumentNodes() { ArrayList<IExpressionNode> retVal = new ArrayList<IExpressionNode>(); if (argumentsNode != null) { int childCount = argumentsNode.getChildCount(); for (int i = 0; i < childCount; i++) { IASNode child = argumentsNode.getChild(i); if (child instanceof IExpressionNode) { retVal.add((IExpressionNode)child); } } } return retVal.toArray(new IExpressionNode[0]); } @Override public boolean isSuperExpression() { return nameNode instanceof ILanguageIdentifierNode && ((ILanguageIdentifierNode)nameNode).getKind() == LanguageIdentifierKind.SUPER; } @Override public ContainerNode getArgumentsNode() { return argumentsNode; } @Override public KeywordNode getNewKeywordNode() { return newKeywordNode; } public void setNewKeywordNode(KeywordNode newNode) { newKeywordNode = newNode; if (newKeywordNode != null) newKeywordNode.setParent(this); } @Override public boolean isCallToSuper() { return nameNode instanceof ILanguageIdentifierNode && nameNode.getAncestorOfType(ClassNode.class) != null && ((ILanguageIdentifierNode)nameNode).getKind() == LanguageIdentifierKind.SUPER && nameNode.getAncestorOfType(MemberAccessExpressionNode.class) == null && getAncestorOfType(FunctionNode.class) != null; } }