All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.htmlunit.corejs.javascript.ast.FunctionNode Maven / Gradle / Ivy

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.htmlunit.corejs.javascript.ast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.htmlunit.corejs.javascript.Node;
import org.htmlunit.corejs.javascript.Token;

/**
 * A JavaScript function declaration or expression.
 *
 * 

Node type is {@link Token#FUNCTION}. * *

FunctionDeclaration :
 *        function Identifier ( FormalParameterListopt ) { FunctionBody }
 * FunctionExpression :
 *        function Identifieropt ( FormalParameterListopt ) { FunctionBody }
 * FormalParameterList :
 *        Identifier
 *        FormalParameterList , Identifier
 * FunctionBody :
 *        SourceElements
 * Program :
 *        SourceElements
 * SourceElements :
 *        SourceElement
 *        SourceElements SourceElement
 * SourceElement :
 *        Statement
 *        FunctionDeclaration
* * JavaScript 1.8 introduces "function closures" of the form * *
function ([params] ) Expression
* * In this case the FunctionNode node will have no body but will have an expression. */ public class FunctionNode extends ScriptNode { /** * There are three types of functions that can be defined. The first is a function statement. * This is a function appearing as a top-level statement (i.e., not nested inside some other * statement) in either a script or a function. * *

The second is a function expression, which is a function appearing in an expression except * for the third type, which is... * *

The third type is a function expression where the expression is the top-level expression * in an expression statement. * *

The three types of functions have different treatment and must be distinguished. */ public static final int FUNCTION_STATEMENT = 1; public static final int FUNCTION_EXPRESSION = 2; public static final int FUNCTION_EXPRESSION_STATEMENT = 3; public static final int ARROW_FUNCTION = 4; public static enum Form { FUNCTION, GETTER, SETTER, METHOD } private static final List NO_PARAMS = Collections.unmodifiableList(new ArrayList<>()); private Name functionName; private List params; private AstNode body; private boolean isExpressionClosure; private Form functionForm = Form.FUNCTION; private int lp = -1; private int rp = -1; private boolean hasRestParameter; // codegen variables private int functionType; private boolean needsActivation; private boolean isGenerator; private boolean isES6Generator; private List generatorResumePoints; private Map liveLocals; private AstNode memberExprNode; { type = Token.FUNCTION; } public FunctionNode() {} public FunctionNode(int pos) { super(pos); } public FunctionNode(int pos, Name name) { super(pos); setFunctionName(name); } /** * Returns function name * * @return function name, {@code null} for anonymous functions */ public Name getFunctionName() { return functionName; } /** * Sets function name, and sets its parent to this node. * * @param name function name, {@code null} for anonymous functions */ public void setFunctionName(Name name) { functionName = name; if (name != null) name.setParent(this); } /** * Returns the function name as a string * * @return the function name, {@code ""} if anonymous */ public String getName() { return functionName != null ? functionName.getIdentifier() : ""; } /** * Returns the function parameter list * * @return the function parameter list. Returns an immutable empty list if there are no * parameters. */ public List getParams() { return params != null ? params : NO_PARAMS; } /** * Sets the function parameter list, and sets the parent for each element of the list. * * @param params the function parameter list, or {@code null} if no params */ public void setParams(List params) { if (params == null) { this.params = null; } else { if (this.params != null) this.params.clear(); for (AstNode param : params) addParam(param); } } /** * Adds a parameter to the function parameter list. Sets the parent of the param node to this * node. * * @param param the parameter * @throws IllegalArgumentException if param is {@code null} */ public void addParam(AstNode param) { assertNotNull(param); if (params == null) { params = new ArrayList<>(); } params.add(param); param.setParent(this); } /** * Returns true if the specified {@link AstNode} node is a parameter of this Function node. This * provides a way during AST traversal to disambiguate the function name node from the parameter * nodes. */ public boolean isParam(AstNode node) { return params == null ? false : params.contains(node); } /** * Returns function body. Normally a {@link Block}, but can be a plain {@link AstNode} if it's a * function closure. * * @return the body. Can be {@code null} only if the AST is malformed. */ public AstNode getBody() { return body; } /** * Sets function body, and sets its parent to this node. Also sets the encoded source bounds * based on the body bounds. Assumes the function node absolute position has already been set, * and the body node's absolute position and length are set. * *

* * @param body function body. Its parent is set to this node, and its position is updated to be * relative to this node. * @throws IllegalArgumentException if body is {@code null} */ public void setBody(AstNode body) { assertNotNull(body); this.body = body; if (Boolean.TRUE.equals(body.getProp(Node.EXPRESSION_CLOSURE_PROP))) { setIsExpressionClosure(true); } int absEnd = body.getPosition() + body.getLength(); body.setParent(this); this.setLength(absEnd - this.position); setEncodedSourceBounds(this.position, absEnd); } /** Returns left paren position, -1 if missing */ public int getLp() { return lp; } /** Sets left paren position */ public void setLp(int lp) { this.lp = lp; } /** Returns right paren position, -1 if missing */ public int getRp() { return rp; } /** Sets right paren position */ public void setRp(int rp) { this.rp = rp; } /** Sets both paren positions */ public void setParens(int lp, int rp) { this.lp = lp; this.rp = rp; } /** Returns whether this is a 1.8 function closure */ public boolean isExpressionClosure() { return isExpressionClosure; } /** Sets whether this is a 1.8 function closure */ public void setIsExpressionClosure(boolean isExpressionClosure) { this.isExpressionClosure = isExpressionClosure; } /** * Return true if this function requires an Ecma-262 Activation object. The Activation object is * implemented by {@link org.htmlunit.corejs.javascript.NativeCall}, and is fairly expensive to create, * so when possible, the interpreter attempts to use a plain call frame instead. * * @return true if this function needs activation. It could be needed if there is a lexical * closure, or in a number of other situations. */ public boolean requiresActivation() { // Special to HtmlUnit's Rhino fork. // return needsActivation; return true; } public void setRequiresActivation() { needsActivation = true; } public boolean isGenerator() { return isGenerator; } public void setIsGenerator() { isGenerator = true; } public boolean isES6Generator() { return isES6Generator; } public void setIsES6Generator() { isES6Generator = true; isGenerator = true; } @Override public boolean hasRestParameter() { return hasRestParameter; } public void setHasRestParameter(boolean hasRestParameter) { this.hasRestParameter = hasRestParameter; } public void addResumptionPoint(Node target) { if (generatorResumePoints == null) generatorResumePoints = new ArrayList<>(); generatorResumePoints.add(target); } public List getResumptionPoints() { return generatorResumePoints; } public Map getLiveLocals() { return liveLocals; } public void addLiveLocals(Node node, int[] locals) { if (liveLocals == null) liveLocals = new HashMap<>(); liveLocals.put(node, locals); } @Override public int addFunction(FunctionNode fnNode) { int result = super.addFunction(fnNode); if (getFunctionCount() > 0) { needsActivation = true; } return result; } /** Returns the function type (statement, expr, statement expr) */ public int getFunctionType() { return functionType; } public void setFunctionType(int type) { functionType = type; } public boolean isMethod() { return functionForm == Form.GETTER || functionForm == Form.SETTER || functionForm == Form.METHOD; } public boolean isGetterMethod() { return functionForm == Form.GETTER; } public boolean isSetterMethod() { return functionForm == Form.SETTER; } public boolean isNormalMethod() { return functionForm == Form.METHOD; } public void setFunctionIsGetterMethod() { functionForm = Form.GETTER; } public void setFunctionIsSetterMethod() { functionForm = Form.SETTER; } public void setFunctionIsNormalMethod() { functionForm = Form.METHOD; } /** * Rhino supports a nonstandard Ecma extension that allows you to say, for instance, function * a.b.c(arg1, arg) {...}, and it will be rewritten at codegen time to: a.b.c = function(arg1, * arg2) {...} If we detect an expression other than a simple Name in the position where a * function name was expected, we record that expression here. * *

This extension is only available by setting the CompilerEnv option * "isAllowMemberExprAsFunctionName" in the Parser. */ public void setMemberExprNode(AstNode node) { memberExprNode = node; if (node != null) node.setParent(this); } public AstNode getMemberExprNode() { return memberExprNode; } @Override public String toSource(int depth) { StringBuilder sb = new StringBuilder(); boolean isArrow = functionType == ARROW_FUNCTION; if (!isMethod()) { sb.append(makeIndent(depth)); if (!isArrow) { sb.append("function"); } } if (functionName != null) { sb.append(" "); sb.append(functionName.toSource(0)); } if (params == null) { sb.append("() "); } else if (isArrow && lp == -1) { // no paren printList(params, sb); sb.append(" "); } else { sb.append("("); printList(params, sb); if (getIntProp(TRAILING_COMMA, 0) == 1) { sb.append(", "); } sb.append(") "); } if (isArrow) { sb.append("=> "); } if (isExpressionClosure) { AstNode body = getBody(); if (body.getLastChild() instanceof ReturnStatement) { // omit "return" keyword, just print the expression body = ((ReturnStatement) body.getLastChild()).getReturnValue(); sb.append(body.toSource(0)); if (functionType == FUNCTION_STATEMENT) { sb.append(";"); } } else { // should never happen sb.append(" "); sb.append(body.toSource(0)); } } else { sb.append(getBody().toSource(depth).trim()); } if (functionType == FUNCTION_STATEMENT || isMethod()) { sb.append("\n"); } return sb.toString(); } /** * Visits this node, the function name node if supplied, the parameters, and the body. If there * is a member-expr node, it is visited last. */ @Override public void visit(NodeVisitor v) { if (v.visit(this)) { if (functionName != null) { functionName.visit(v); } for (AstNode param : getParams()) { param.visit(v); } getBody().visit(v); if (!isExpressionClosure) { if (memberExprNode != null) { memberExprNode.visit(v); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy