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