1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 package org.mozilla.javascript.ast;
8 
9 import org.mozilla.javascript.Node;
10 import org.mozilla.javascript.Token;
11 
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 
18 /**
19  * A JavaScript function declaration or expression.<p>
20  * Node type is {@link Token#FUNCTION}.<p>
21  *
22  * <pre><i>FunctionDeclaration</i> :
23  *        <b>function</b> Identifier ( FormalParameterListopt ) { FunctionBody }
24  * <i>FunctionExpression</i> :
25  *        <b>function</b> Identifieropt ( FormalParameterListopt ) { FunctionBody }
26  * <i>FormalParameterList</i> :
27  *        Identifier
28  *        FormalParameterList , Identifier
29  * <i>FunctionBody</i> :
30  *        SourceElements
31  * <i>Program</i> :
32  *        SourceElements
33  * <i>SourceElements</i> :
34  *        SourceElement
35  *        SourceElements SourceElement
36  * <i>SourceElement</i> :
37  *        Statement
38  *        FunctionDeclaration</pre>
39  *
40  * JavaScript 1.8 introduces "function closures" of the form
41  *  <pre>function ([params] ) Expression</pre>
42  *
43  * In this case the FunctionNode node will have no body but will have an
44  * expression.
45  */
46 public class FunctionNode extends ScriptNode {
47 
48     /**
49      * There are three types of functions that can be defined. The first
50      * is a function statement. This is a function appearing as a top-level
51      * statement (i.e., not nested inside some other statement) in either a
52      * script or a function.<p>
53      *
54      * The second is a function expression, which is a function appearing in
55      * an expression except for the third type, which is...<p>
56      *
57      * The third type is a function expression where the expression is the
58      * top-level expression in an expression statement.<p>
59      *
60      * The three types of functions have different treatment and must be
61      * distinguished.<p>
62      */
63     public static final int FUNCTION_STATEMENT            = 1;
64     public static final int FUNCTION_EXPRESSION           = 2;
65     public static final int FUNCTION_EXPRESSION_STATEMENT = 3;
66 
67     public static enum Form { FUNCTION, GETTER, SETTER }
68 
69     private static final List<AstNode> NO_PARAMS =
70         Collections.unmodifiableList(new ArrayList<AstNode>());
71 
72     private Name functionName;
73     private List<AstNode> params;
74     private AstNode body;
75     private boolean isExpressionClosure;
76     private Form functionForm = Form.FUNCTION;
77     private int lp = -1;
78     private int rp = -1;
79 
80     // codegen variables
81     private int functionType;
82     private boolean needsActivation;
83     private boolean isGenerator;
84     private List<Node> generatorResumePoints;
85     private Map<Node,int[]> liveLocals;
86     private AstNode memberExprNode;
87 
88     {
89         type = Token.FUNCTION;
90     }
91 
FunctionNode()92     public FunctionNode() {
93     }
94 
FunctionNode(int pos)95     public FunctionNode(int pos) {
96         super(pos);
97     }
98 
FunctionNode(int pos, Name name)99     public FunctionNode(int pos, Name name) {
100         super(pos);
101         setFunctionName(name);
102     }
103 
104     /**
105      * Returns function name
106      * @return function name, {@code null} for anonymous functions
107      */
getFunctionName()108     public Name getFunctionName() {
109         return functionName;
110     }
111 
112     /**
113      * Sets function name, and sets its parent to this node.
114      * @param name function name, {@code null} for anonymous functions
115      */
setFunctionName(Name name)116     public void setFunctionName(Name name) {
117         functionName = name;
118         if (name != null)
119             name.setParent(this);
120     }
121 
122     /**
123      * Returns the function name as a string
124      * @return the function name, {@code ""} if anonymous
125      */
getName()126     public String getName() {
127         return functionName != null ? functionName.getIdentifier() : "";
128     }
129 
130     /**
131      * Returns the function parameter list
132      * @return the function parameter list.  Returns an immutable empty
133      *         list if there are no parameters.
134      */
getParams()135     public List<AstNode> getParams() {
136         return params != null ? params : NO_PARAMS;
137     }
138 
139     /**
140      * Sets the function parameter list, and sets the parent for
141      * each element of the list.
142      * @param params the function parameter list, or {@code null} if no params
143      */
setParams(List<AstNode> params)144     public void setParams(List<AstNode> params) {
145         if (params == null) {
146             this.params = null;
147         } else {
148             if (this.params != null)
149                 this.params.clear();
150             for (AstNode param : params)
151                 addParam(param);
152         }
153     }
154 
155     /**
156      * Adds a parameter to the function parameter list.
157      * Sets the parent of the param node to this node.
158      * @param param the parameter
159      * @throws IllegalArgumentException if param is {@code null}
160      */
addParam(AstNode param)161     public void addParam(AstNode param) {
162         assertNotNull(param);
163         if (params == null) {
164             params = new ArrayList<AstNode>();
165         }
166         params.add(param);
167         param.setParent(this);
168     }
169 
170     /**
171      * Returns true if the specified {@link AstNode} node is a parameter
172      * of this Function node.  This provides a way during AST traversal
173      * to disambiguate the function name node from the parameter nodes.
174      */
isParam(AstNode node)175     public boolean isParam(AstNode node) {
176         return params == null ? false : params.contains(node);
177     }
178 
179     /**
180      * Returns function body.  Normally a {@link Block}, but can be a plain
181      * {@link AstNode} if it's a function closure.
182      *
183      * @return the body.  Can be {@code null} only if the AST is malformed.
184      */
getBody()185     public AstNode getBody() {
186         return body;
187     }
188 
189     /**
190      * Sets function body, and sets its parent to this node.
191      * Also sets the encoded source bounds based on the body bounds.
192      * Assumes the function node absolute position has already been set,
193      * and the body node's absolute position and length are set.<p>
194      *
195      * @param body function body.  Its parent is set to this node, and its
196      * position is updated to be relative to this node.
197      *
198      * @throws IllegalArgumentException if body is {@code null}
199      */
setBody(AstNode body)200     public void setBody(AstNode body) {
201         assertNotNull(body);
202         this.body = body;
203         if (Boolean.TRUE.equals(body.getProp(Node.EXPRESSION_CLOSURE_PROP))) {
204             setIsExpressionClosure(true);
205         }
206         int absEnd = body.getPosition() + body.getLength();
207         body.setParent(this);
208         this.setLength(absEnd - this.position);
209         setEncodedSourceBounds(this.position, absEnd);
210     }
211 
212     /**
213      * Returns left paren position, -1 if missing
214      */
getLp()215     public int getLp() {
216         return lp;
217     }
218 
219     /**
220      * Sets left paren position
221      */
setLp(int lp)222     public void setLp(int lp) {
223         this.lp = lp;
224     }
225 
226     /**
227      * Returns right paren position, -1 if missing
228      */
getRp()229     public int getRp() {
230         return rp;
231     }
232 
233     /**
234      * Sets right paren position
235      */
setRp(int rp)236     public void setRp(int rp) {
237         this.rp = rp;
238     }
239 
240     /**
241      * Sets both paren positions
242      */
setParens(int lp, int rp)243     public void setParens(int lp, int rp) {
244         this.lp = lp;
245         this.rp = rp;
246     }
247 
248     /**
249      * Returns whether this is a 1.8 function closure
250      */
isExpressionClosure()251     public boolean isExpressionClosure() {
252         return isExpressionClosure;
253     }
254 
255     /**
256      * Sets whether this is a 1.8 function closure
257      */
setIsExpressionClosure(boolean isExpressionClosure)258     public void setIsExpressionClosure(boolean isExpressionClosure) {
259         this.isExpressionClosure = isExpressionClosure;
260     }
261 
262     /**
263      * Return true if this function requires an Ecma-262 Activation object.
264      * The Activation object is implemented by
265      * {@link org.mozilla.javascript.NativeCall}, and is fairly expensive
266      * to create, so when possible, the interpreter attempts to use a plain
267      * call frame instead.
268      *
269      * @return true if this function needs activation.  It could be needed
270      * if there is a lexical closure, or in a number of other situations.
271      */
requiresActivation()272     public boolean requiresActivation() {
273         return needsActivation;
274     }
275 
setRequiresActivation()276     public void setRequiresActivation() {
277         needsActivation = true;
278     }
279 
isGenerator()280     public boolean isGenerator() {
281       return isGenerator;
282     }
283 
setIsGenerator()284     public void setIsGenerator() {
285         isGenerator = true;
286     }
287 
addResumptionPoint(Node target)288     public void addResumptionPoint(Node target) {
289         if (generatorResumePoints == null)
290             generatorResumePoints = new ArrayList<Node>();
291         generatorResumePoints.add(target);
292     }
293 
getResumptionPoints()294     public List<Node> getResumptionPoints() {
295         return generatorResumePoints;
296     }
297 
getLiveLocals()298     public Map<Node,int[]> getLiveLocals() {
299         return liveLocals;
300     }
301 
addLiveLocals(Node node, int[] locals)302     public void addLiveLocals(Node node, int[] locals) {
303         if (liveLocals == null)
304             liveLocals = new HashMap<Node,int[]>();
305         liveLocals.put(node, locals);
306     }
307 
308     @Override
addFunction(FunctionNode fnNode)309     public int addFunction(FunctionNode fnNode) {
310         int result = super.addFunction(fnNode);
311         if (getFunctionCount() > 0) {
312             needsActivation = true;
313         }
314         return result;
315     }
316 
317     /**
318      * Returns the function type (statement, expr, statement expr)
319      */
getFunctionType()320     public int getFunctionType() {
321         return functionType;
322     }
323 
setFunctionType(int type)324     public void setFunctionType(int type) {
325         functionType = type;
326     }
327 
isGetterOrSetter()328     public boolean isGetterOrSetter() {
329         return functionForm == Form.GETTER || functionForm == Form.SETTER;
330     }
331 
isGetter()332     public boolean isGetter() {
333         return functionForm == Form.GETTER;
334     }
335 
isSetter()336     public boolean isSetter() {
337         return functionForm == Form.SETTER;
338     }
339 
setFunctionIsGetter()340     public void setFunctionIsGetter() {
341         functionForm = Form.GETTER;
342     }
343 
setFunctionIsSetter()344     public void setFunctionIsSetter() {
345         functionForm = Form.SETTER;
346     }
347 
348     /**
349      * Rhino supports a nonstandard Ecma extension that allows you to
350      * say, for instance, function a.b.c(arg1, arg) {...}, and it will
351      * be rewritten at codegen time to:  a.b.c = function(arg1, arg2) {...}
352      * If we detect an expression other than a simple Name in the position
353      * where a function name was expected, we record that expression here.
354      * <p>
355      * This extension is only available by setting the CompilerEnv option
356      * "isAllowMemberExprAsFunctionName" in the Parser.
357      */
setMemberExprNode(AstNode node)358     public void setMemberExprNode(AstNode node) {
359         memberExprNode = node;
360         if (node != null)
361             node.setParent(this);
362     }
363 
getMemberExprNode()364     public AstNode getMemberExprNode() {
365         return memberExprNode;
366     }
367 
368     @Override
toSource(int depth)369     public String toSource(int depth) {
370         StringBuilder sb = new StringBuilder();
371         sb.append(makeIndent(depth));
372         sb.append("function");
373         if (functionName != null) {
374             sb.append(" ");
375             sb.append(functionName.toSource(0));
376         }
377         if (params == null) {
378             sb.append("() ");
379         } else {
380             sb.append("(");
381             printList(params, sb);
382             sb.append(") ");
383         }
384         if (isExpressionClosure) {
385             AstNode body = getBody();
386             if (body.getLastChild() instanceof ReturnStatement) {
387                 // omit "return" keyword, just print the expression
388                 body = ((ReturnStatement) body.getLastChild()).getReturnValue();
389                 sb.append(body.toSource(0));
390                 if (functionType == FUNCTION_STATEMENT) {
391                     sb.append(";");
392                 }
393             } else {
394                 // should never happen
395                 sb.append(" ");
396                 sb.append(body.toSource(0));
397             }
398         } else {
399             sb.append(getBody().toSource(depth).trim());
400         }
401         if (functionType == FUNCTION_STATEMENT) {
402             sb.append("\n");
403         }
404         return sb.toString();
405     }
406 
407     /**
408      * Visits this node, the function name node if supplied,
409      * the parameters, and the body.  If there is a member-expr node,
410      * it is visited last.
411      */
412     @Override
visit(NodeVisitor v)413     public void visit(NodeVisitor v) {
414         if (v.visit(this)) {
415             if (functionName != null) {
416                 functionName.visit(v);
417             }
418             for (AstNode param : getParams()) {
419                 param.visit(v);
420             }
421             getBody().visit(v);
422             if (!isExpressionClosure) {
423                 if (memberExprNode != null) {
424                     memberExprNode.visit(v);
425                 }
426             }
427         }
428     }
429 }
430